深入理解Java虚拟机 第2版 周志明著(三)
第3章 垃圾收集器與內(nèi)存分配策略
3.1 如何判定對(duì)象是“活著”或者“死去”?
-
引用計(jì)數(shù)法:給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器就加1,當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1,任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不在被使用的對(duì)象。
缺點(diǎn):很難解決循環(huán)引用問(wèn)題。 -
可達(dá)性分析算法:通過(guò)一系列的稱(chēng)為"GC Roots"的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索所走過(guò)的路徑稱(chēng)為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí),這個(gè)對(duì)象就是不可用的。
可作為GC Roots的對(duì)象有:
1.虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
2.方法區(qū)中類(lèi)靜態(tài)變量屬性引用的對(duì)象。
3.方法區(qū)中常量引用的對(duì)象。
3.2 引用
Java中引用的定義:如果reference類(lèi)型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱(chēng)這塊內(nèi)存代表著一個(gè)引用。
在定義之下對(duì)象只有被引用或者沒(méi)有被引用兩種狀態(tài),但是我們希望有一些中間態(tài)的對(duì)象,當(dāng)空間足夠的時(shí)候保留在內(nèi)存中,如果內(nèi)存空間在進(jìn)行垃圾收集后還是非常緊張的話(huà)可以?huà)仐夁@些對(duì)象。所以Java中才有了,強(qiáng)引用,軟引用,弱引用,虛引用。
3.3 垃圾收集算法
首先標(biāo)記出所有需要回收的對(duì)象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象。
缺點(diǎn):效率低,會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,可能會(huì)導(dǎo)致需要分配較大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)一次GC。
將可用內(nèi)存的容量劃分為大小相等的兩塊,每次只用其中一塊,當(dāng)這一塊的內(nèi)存用完了,就將還活著的對(duì)象復(fù)制到另外一塊上,然后再把使用過(guò)的這塊清空。
缺點(diǎn):內(nèi)存縮減為原來(lái)的一半。
大部分虛擬機(jī)都是用這種算法,但是內(nèi)存比例是按照8:1:1來(lái)分配的。即將內(nèi)存分為較大的Eden空間,和兩塊較小的From Survivor和To Survivor空間,每次使用Eden和From Survivor。當(dāng)回收時(shí),將Eden和From Survivor中還存活的對(duì)象一次性的復(fù)制到To Survivor中,最后清理掉Eden和From Survivor。當(dāng)To Survivor內(nèi)存空間不夠用來(lái)收集存活的對(duì)象時(shí),需要依賴(lài)?yán)夏甏M(jìn)行分配擔(dān)保。
首先標(biāo)記出所有需要回收的對(duì)象,然后讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉邊界以外的內(nèi)存。
把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/li>
3.4 垃圾收集器
Serial收集器
單線程收集器,它在進(jìn)行垃圾收集時(shí)必須暫停其他所有的工作線程,直到它收集結(jié)束。它是虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)新生代收集器。
算法:復(fù)制算法
ParNew收集器
就是Serial收集器的多線程版本,目前只有它可以和CMS收集器配合工作。單CPU的環(huán)境中效果一定不會(huì)有Serial收集器效果好,雙CPU也不一定能保證。但是CPU越多效果越好。默認(rèn)開(kāi)啟的收集線程數(shù)與CPU數(shù)量相同。
算法:復(fù)制算法
Parallel Scavenge收集器
新生代的收集器,又是并行的多線程收集器,特點(diǎn)是它的關(guān)注點(diǎn)與其他的收集器不同,其他的收集器的關(guān)注點(diǎn)是盡可能的縮短垃圾收集時(shí)間用戶(hù)線程的停頓時(shí)間。而Parallel Scavenge收集器的目標(biāo)則是達(dá)到一個(gè)可控制的吞吐量。吞吐量是CPU用戶(hù)運(yùn)行代碼的時(shí)間與CPU總消耗時(shí)間的比值。公式:
吞吐量 = 運(yùn)行用戶(hù)代碼時(shí)間 / (運(yùn)行用戶(hù)代碼時(shí)間 + 垃圾收集時(shí)間)
Parallel Scavenge收集器與ParNew還有一個(gè)重要區(qū)別就是Parallel Scavenge收集器可以設(shè)置自適應(yīng)的調(diào)節(jié)策略。Parallel Scavenge收集器架構(gòu)中是有收集器來(lái)進(jìn)行老年代收集的,但是這個(gè)收集器的實(shí)現(xiàn)和Serial Old非常接近
Serial Old收集器
Serial Old是Serial收集器的老年代版本,同樣是單線程收集器,這個(gè)收集器的主要意義在于給Client模式下的虛擬機(jī)使用,一般作為CMS收集器的后備預(yù)案。
算法:標(biāo)記 - 整理
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本。在注重吞吐量以及CPU資源敏感的場(chǎng)合,都可以?xún)?yōu)先考慮Parallel Scavenge收集器加Parallel Old收集器的組合。
算法:標(biāo)記 - 整理
CMS收集器
老年代收集器,CMS收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。優(yōu)點(diǎn),并發(fā)收集,低停頓。
算法:標(biāo)記 - 清除
運(yùn)作過(guò)程分為:初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,并發(fā)清除4個(gè)步驟。其中初始標(biāo)記和重新標(biāo)記這兩個(gè)步驟仍然需要Stop The World。
初始標(biāo)記:僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快、
并發(fā)標(biāo)記:就是進(jìn)行CG Roots Tracing(找引用鏈)的過(guò)程。
重新標(biāo)記:是為了修正并發(fā)標(biāo)記期間因用戶(hù)程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄。這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記的時(shí)間長(zhǎng),但是遠(yuǎn)低于并發(fā)標(biāo)記的時(shí)間。
并發(fā)清除:回收垃圾。
缺點(diǎn):1.雖然不會(huì)導(dǎo)致用戶(hù)線程停頓,但會(huì)因?yàn)檎加昧艘徊糠志€程導(dǎo)致應(yīng)用程序變慢,總吞吐量降低。2.CMS無(wú)法處理浮動(dòng)垃圾(并發(fā)清除中用戶(hù)線程還在運(yùn)行,也就還會(huì)有新的垃圾出現(xiàn),這一部分垃圾沒(méi)有被標(biāo)記過(guò),無(wú)法在當(dāng)次收集中處理掉,這一部分垃圾稱(chēng)為浮動(dòng)垃圾)。正是因?yàn)榍宄臅r(shí)候用戶(hù)線程還在運(yùn)行,所以需要預(yù)留一部分空間給用戶(hù)線程在這期間產(chǎn)生的垃圾使用,如果預(yù)留的空間不夠用,虛擬就就會(huì)臨時(shí)啟用Serial Old收集器來(lái)重新進(jìn)行老年代的垃圾收集,停頓時(shí)間就很長(zhǎng)了。3.由于算法實(shí)現(xiàn)的原因會(huì)產(chǎn)生大量的空間碎片。
G1收集器
面向服務(wù)端的收集器,優(yōu)點(diǎn):并行與并發(fā),分代收集,空間整合,可預(yù)測(cè)停頓。它將整個(gè)java堆劃分為多個(gè)大小相等的獨(dú)立的區(qū)域(Region),新生代和老年代不再是物理隔離,他們都是一部分Region(不需要連續(xù))的集合。
運(yùn)作過(guò)程分為:初始標(biāo)記,并發(fā)標(biāo)記,最終標(biāo)記,篩選回收4個(gè)步驟。
3.5 內(nèi)存分配
1.對(duì)象優(yōu)先在Eden分配(當(dāng)Eden沒(méi)有足夠空間時(shí),虛擬機(jī)發(fā)起一次Minor GC)。
2.大對(duì)象直接進(jìn)入老年代。
3.長(zhǎng)期存活的對(duì)象進(jìn)入老年代(默認(rèn)年齡15)。
4.如果在Survivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或者等于該年齡的對(duì)象可以直接進(jìn)入老年代。
5.空間分配擔(dān)保:在發(fā)生Minor GC之前,虛擬機(jī)會(huì)檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果大于,那Minor GC安全。如果小于,接著查看HandlePromotionFailure的值是否允許失敗,如果設(shè)置允許失敗,會(huì)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于,嘗試進(jìn)行一次Minor GC。如果小于或者HandlePromotionFailure的值設(shè)置不允許,那這時(shí)需要進(jìn)行一次Full GC。
總結(jié)
以上是生活随笔為你收集整理的深入理解Java虚拟机 第2版 周志明著(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【第64期】豆瓣9.8分,周志明的《凤凰
- 下一篇: 《深入理解Java虚拟机》-周志明(转)