目錄
1. when what how
2. 垃圾回收器羅列
3. G1算法詳解
1. when what how
When
when 分為Minor GC 和 Full GC
Minor GC觸發(fā)條件:當(dāng)Eden區(qū)滿時,觸發(fā)Minor GC软啼。
Full GC觸發(fā)條件:
(1)調(diào)用System.gc時音念,系統(tǒng)建議執(zhí)行Full GC怎憋,但是不必然執(zhí)行
(2)老年代空間不足
(3)方法區(qū)空間不足
(4)通過Minor GC后進入老年代的平均大小大于老年代的可用內(nèi)存
(5)由Eden區(qū)蚊伞、From Space區(qū)向To Space區(qū)復(fù)制時叫编,對象大小大于To Space可用內(nèi)存杯活,則把該對象轉(zhuǎn)存到老年代,且老年代的可用內(nèi)存小于該對象大小
What
這里你們需要知道2種標(biāo)記垃圾的算法蜂绎。引用計數(shù)法栅表,和 根搜索算法。
1师枣、引用計數(shù)法
這個算法的實現(xiàn)是怪瓶,給對象中添加一個引用計數(shù)器,每當(dāng)一個地方引用這個對象時践美,計數(shù)器值+1洗贰;當(dāng)引用失效時,計數(shù)器值-1陨倡。任何時刻計數(shù)值為0的對象就是不可能再被使用的敛滋。這種算法使用場景很多,但是玫膀,Java中卻沒有使用這種算法矛缨,因為這種算法很難解決對象之間相互引用的情況
2爹脾、根搜索算法
這個算法的基本思想是通過一系列稱為“GC Roots”的對象作為起始點帖旨,從這些節(jié)點向下搜索,搜索所走過的路徑稱為引用鏈灵妨,當(dāng)一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達)時解阅,則證明此對象是不可用的。
那么問題又來了泌霍,如何選取GCRoots對象呢货抄?在Java語言中述召,可以作為GCRoots的對象包括下面幾種:
(1). 虛擬機棧(棧幀中的局部變量區(qū),也叫做局部變量表)中引用的對象蟹地。
(2). 方法區(qū)中的類靜態(tài)屬性引用的對象积暖。
(3). 方法區(qū)中常量引用的對象。
(4). 本地方法棧中JNI(Native方法)引用的對象怪与。
下面給出一個GCRoots的例子夺刑,如下圖,為GCRoots的引用鏈分别。
由圖可知遍愿,obj8、obj9耘斩、obj10都沒有到GCRoots對象的引用鏈沼填,即便obj9和obj10之間有引用鏈,他們還是會被當(dāng)成垃圾處理括授,可以進行回收坞笙。
這里要補充一點,通過根搜索算法之后刽脖,那些沒有達到的對象只是標(biāo)記為可復(fù)活狀態(tài)羞海,也就是他們還有一次復(fù)活的機會,如下:
1. 如果對象在進行可達性分析后發(fā)現(xiàn)沒有與GCRoots相連的引用鏈曲管,則該對象被第一次標(biāo)記并進行一次篩選却邓,篩選條件為是否有必要執(zhí)行該對象的finalize方法,若對象沒有覆蓋finalize方法或者該finalize方法是否已經(jīng)被虛擬機執(zhí)行過了院水,則均視作不必要執(zhí)行該對象的finalize方法腊徙,即該對象將會被回收。反之檬某,若對象覆蓋了finalize方法并且該finalize方法并沒有被執(zhí)行過撬腾,那么,這個對象會被放置在一個叫F-Queue的隊列中恢恼,之后會由虛擬機自動建立的民傻、優(yōu)先級低的Finalizer線程去執(zhí)行,而虛擬機不必要等待該線程執(zhí)行結(jié)束场斑,即虛擬機只負責(zé)建立線程漓踢,其他的事情交給此線程去處理。
2.對F-Queue中對象進行第二次標(biāo)記漏隐,如果對象在finalize方法中拯救了自己喧半,即關(guān)聯(lián)上了GCRoots引用鏈,如把this關(guān)鍵字賦值給其他變量青责,那么在第二次標(biāo)記的時候該對象將從“即將回收”的集合中移除挺据,如果對象還是沒有拯救自己取具,那就會被回收。如下代碼演示了一個對象如何在finalize方法中拯救了自己扁耐,然而暇检,它只能拯救自己一次,第二次就被回收了婉称。
擴展4種引用類型
how
1占哟、標(biāo)記-清除(Mark-Sweep)算法
這是最基礎(chǔ)的算法,標(biāo)記-清除算法就如同它的名字樣酿矢,分為“標(biāo)記”和“清除”兩個階段:首先標(biāo)記出所有需要回收的對象榨乎,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。這種算法的不足主要體現(xiàn)在效率和空間瘫筐,從效率的角度講蜜暑,標(biāo)記和清除兩個過程的效率都不高;從空間的角度講策肝,標(biāo)記清除后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片肛捍, 內(nèi)存碎片太多可能會導(dǎo)致以后程序運行過程中在需要分配較大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)一次垃圾收集動作之众。標(biāo)記-清除算法執(zhí)行過程如圖:
2拙毫、復(fù)制(Copying)算法
復(fù)制算法是為了解決效率問題而出現(xiàn)的,它將可用的內(nèi)存分為兩塊棺禾,每次只用其中一塊缀蹄,當(dāng)這一塊內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面膘婶,然后再把已經(jīng)使用過的內(nèi)存空間一次性清理掉缺前。這樣每次只需要對整個半?yún)^(qū)進行內(nèi)存回收,內(nèi)存分配時也不需要考慮內(nèi)存碎片等復(fù)雜情況悬襟,只需要移動指針衅码,按照順序分配即可。復(fù)制算法的執(zhí)行過程如圖:
不過這種算法有個缺點脊岳,內(nèi)存縮小為了原來的一半逝段,這樣代價太高了。現(xiàn)在的商用虛擬機都采用這種算法來回收新生代割捅,不過研究表明1:1的比例非常不科學(xué)奶躯,因此新生代的內(nèi)存被劃分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor棺牧。每次回收時巫糙,將Eden和Survivor中還存活著的對象一次性復(fù)制到另外一塊Survivor空間上朗儒,最后清理掉Eden和剛才用過的Survivor空間颊乘。HotSpot虛擬機默認Eden區(qū)和Survivor區(qū)的比例為8:1参淹,意思是每次新生代中可用內(nèi)存空間為整個新生代容量的90%。當(dāng)然乏悄,我們沒有辦法保證每次回收都只有不多于10%的對象存活浙值,當(dāng)Survivor空間不夠用時,需要依賴?yán)夏甏M行分配擔(dān)保(Handle Promotion)檩小。
3开呐、標(biāo)記-整理(Mark-Compact)算法
復(fù)制算法在對象存活率較高的場景下要進行大量的復(fù)制操作,效率很低规求。萬一對象100%存活筐付,那么需要有額外的空間進行分配擔(dān)保。老年代都是不易被回收的對象阻肿,對象存活率高瓦戚,因此一般不能直接選用復(fù)制算法。根據(jù)老年代的特點丛塌,有人提出了另外一種標(biāo)記-整理算法较解,過程與標(biāo)記-清除算法一樣,不過不是直接對可回收對象進行清理赴邻,而是讓所有存活對象都向一端移動印衔,然后直接清理掉邊界以外的內(nèi)存。標(biāo)記-整理算法的工作過程如圖:
4姥敛、分代收集算法
根據(jù)上面的內(nèi)容奸焙,用一張圖概括一下堆內(nèi)存的布局
現(xiàn)代商用虛擬機基本都采用分代收集算法來進行垃圾回收。這種算法沒什么特別的彤敛,無非是上面內(nèi)容的結(jié)合罷了忿偷,根據(jù)對象的生命周期的不同將內(nèi)存劃分為幾塊,然后根據(jù)各塊的特點采用最適當(dāng)?shù)氖占惴凇4笈鷮ο笏廊ダ鹎拧⑸倭繉ο蟠婊畹模ㄐ律褂脧?fù)制算法茶凳,復(fù)制成本低播揪;對象存活率高、沒有額外空間進行分配擔(dān)保的(老年代)箱沦,采用標(biāo)記-清理算法或者標(biāo)記-整理算法雇庙。
2. 垃圾回收器羅列
HotSpot這個虛擬機所包含的所有收集器如圖:
上圖展示了7種作用于不同分代的收集器灶伊,如果兩個收集器之間存在連線祖娘,那說明它們可以搭配使用超燃。虛擬機所處的區(qū)域說明它是屬于新生代收集器還是老年代收集器。
1朦促、Serial收集器
最基本童太、發(fā)展歷史最久的收集器米辐,這個收集器是一個采用復(fù)制算法的單線程的收集器,單線程一方面意味著它只會使用一個CPU或一條線程去完成垃圾收集工作书释,另一方面也意味著它進行垃圾收集時必須暫停其他線程的所有工作翘贮,直到它收集結(jié)束為止。后者意味著爆惧,在用戶不可見的情況下要把用戶正常工作的線程全部停掉择膝,這對很多應(yīng)用是難以接受的。不過實際上到目前為止检激,Serial收集器依然是虛擬機運行在Client模式下的默認新生代收集器肴捉,因為它簡單而高效。用戶桌面應(yīng)用場景中叔收,分配給虛擬機管理的內(nèi)存一般來說不會很大齿穗,收集幾十兆甚至一兩百兆的新生代停頓時間在幾十毫秒最多一百毫秒,只要不是頻繁發(fā)生饺律,這點停頓是完全可以接受的窃页。Serial收集器運行過程如下圖所示:
說明:1. 需要STW(Stop The World),停頓時間長复濒。2. 簡單高效脖卖,對于單個CPU環(huán)境而言,Serial收集器由于沒有線程交互開銷巧颈,可以獲取最高的單線程收集效率畦木。
2、ParNew收集器
ParNew收集器其實就是Serial收集器的多線程版本砸泛,除了使用多條線程進行垃圾收集外十籍,其余行為和Serial收集器完全一樣勾栗,包括使用的也是復(fù)制算法。ParNew收集器除了多線程以外和Serial收集器并沒有太多創(chuàng)新的地方围俘,但是它卻是Server模式下的虛擬機首選的新生代收集器,其中有一個很重要的和性能無關(guān)的原因是簿寂,除了Serial收集器外,目前只有它能與CMS收集器配合工作(看圖)奋蔚。CMS收集器是一款幾乎可以認為有劃時代意義的垃圾收集器,因為它第一次實現(xiàn)了讓垃圾收集線程與用戶線程基本上同時工作泊碑。ParNew收集器在單CPU的環(huán)境中絕對不會有比Serial收集器更好的效果,甚至由于線程交互的開銷臭脓,該收集器在兩個CPU的環(huán)境中都不能百分之百保證可以超越Serial收集器腹忽。當(dāng)然,隨著可用CPU數(shù)量的增加嘹锁,它對于GC時系統(tǒng)資源的有效利用還是很有好處的领猾。它默認開啟的收集線程數(shù)與CPU數(shù)量相同骇扇,在CPU數(shù)量非常多的情況下,可以使用-XX:ParallelGCThreads參數(shù)來限制垃圾收集的線程數(shù)继低。ParNew收集器運行過程如下圖所示:
3稍走、Parallel Scavenge收集器
??Parallel Scavenge收集器也是一個新生代收集器,也是用復(fù)制算法的收集器梦裂,也是并行的多線程收集器年柠,但是它的特點是它的關(guān)注點和其他收集器不同。介紹這個收集器主要還是介紹吞吐量的概念答憔。CMS等收集器的關(guān)注點是盡可能縮短垃圾收集時用戶線程的停頓時間掀抹,而Parallel Scavenge收集器的目標(biāo)則是打到一個可控制的吞吐量。所謂吞吐量的意思就是CPU用于運行用戶代碼時間與CPU總消耗時間的比值傲武,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總運行100分鐘态兴,垃圾收集1分鐘瞻润,那吞吐量就是99%甜刻。另外,Parallel?Scavenge收集器是虛擬機運行在Server模式下的默認垃圾收集器楚午。
? ? ?停頓時間短適合需要與用戶交互的程序尿招,良好的響應(yīng)速度能提升用戶體驗;高吞吐量則可以高效率利用CPU時間怪蔑,盡快完成運算任務(wù)丧荐,主要適合在后臺運算而不需要太多交互的任務(wù)。
虛擬機提供了-XX:MaxGCPauseMillis和-XX:GCTimeRatio兩個參數(shù)來精確控制最大垃圾收集停頓時間和吞吐量大小弓坞。不過不要以為前者越小越好渡冻,GC停頓時間的縮短是以犧牲吞吐量和新生代空間換取的忧便。由于與吞吐量關(guān)系密切,Parallel Scavenge收集器也被稱為“吞吐量優(yōu)先收集器”超歌。Parallel Scavenge收集器有一個-XX:+UseAdaptiveSizePolicy參數(shù),這是一個開關(guān)參數(shù)脆荷,這個參數(shù)打開之后懊悯,就不需要手動指定新生代大小、Eden區(qū)和Survivor參數(shù)等細節(jié)參數(shù)了定枷,虛擬機會根據(jù)當(dāng)前系統(tǒng)的運行情況手機性能監(jiān)控信息届氢,動態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時間或者最大的吞吐量。如果對于垃圾收集器運作原理不太了解岖妄,以至于在優(yōu)化比較困難的時候寂祥,使用Parallel Scavenge收集器配合自適應(yīng)調(diào)節(jié)策略,把內(nèi)存管理的調(diào)優(yōu)任務(wù)交給虛擬機去完成將是一個不錯的選擇丸凭。
4、Serial Old收集器
Serial收集器的老年代版本铛碑,同樣是一個單線程收集器虽界,使用“標(biāo)記-整理算法”,這個收集器的主要意義也是在于給Client模式下的虛擬機使用撇吞。
5礁叔、Parallel Old收集器
Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法琅关。這個收集器在JDK 1.6之后的出現(xiàn),“吞吐量優(yōu)先收集器”終于有了比較名副其實的應(yīng)用組合人乓,在注重吞吐量以及CPU資源敏感的場合,都可以優(yōu)先考慮Parallel Scavenge收集器+Parallel Old收集器的組合碰缔。運行過程如下圖所示:
6金抡、CMS收集器
CMS(Conrrurent Mark Sweep)收集器是以獲取最短回收停頓時間為目標(biāo)的收集器腌且。使用標(biāo)記 - 清除算法,收集過程分為如下四步:
(1). 初始標(biāo)記铺董,標(biāo)記GCRoots能直接關(guān)聯(lián)到的對象,時間很短坝锰。
(2). 并發(fā)標(biāo)記重付,進行GCRoots Tracing(可達性分析)過程,時間很長确垫。
(3). 重新標(biāo)記,修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄翔冀,時間較長爬迟。
(4). 并發(fā)清除,回收內(nèi)存空間计福,時間很長徽职。
其中,并發(fā)標(biāo)記與并發(fā)清除兩個階段耗時最長说订,但是可以與用戶線程并發(fā)執(zhí)行。運行過程如下圖所示:
說明:1. 對CPU資源非常敏感陶冷,可能會導(dǎo)致應(yīng)用程序變慢,吞吐率下降煞额。2. 無法處理浮動垃圾沾谜,因為在并發(fā)清理階段用戶線程還在運行,自然就會產(chǎn)生新的垃圾婚温,而在此次收集中無法收集他們媳否,只能留到下次收集,這部分垃圾為浮動垃圾逆日,同時萄凤,由于用戶線程并發(fā)執(zhí)行,所以需要預(yù)留一部分老年代空間提供并發(fā)收集時程序運行使用坪圾。3. 由于采用的標(biāo)記 - 清除算法惑朦,會產(chǎn)生大量的內(nèi)存碎片,不利于大對象的分配漾月,可能會提前觸發(fā)一次Full GC。虛擬機提供了-XX:+UseCMSCompactAtFullCollection參數(shù)來進行碎片的合并整理過程蜓陌,這樣會使得停頓時間變長吩蔑,虛擬機還提供了一個參數(shù)配置,-XX:+CMSFullGCsBeforeCompaction隧期,用于設(shè)置執(zhí)行多少次不壓縮的Full GC后,接著來一次帶壓縮的GC仆潮。