一、基礎(chǔ)性的概念
1呛牲、Minor GC 和 Full GC
新生代GC(Minor GC):指發(fā)生在新生代的垃圾收集動作鞍恢,因?yàn)镴ava對象大多都具備朝生夕滅的特性希太,所以Minor GC非常頻繁狼速,一般回收速度也比較快琅锻。
老年代GC(Major GC / Full GC):指發(fā)生在老年代的GC,出現(xiàn)了Major GC向胡,經(jīng)常會伴隨至少一次的Minor GC(但非絕對的恼蓬,在Parallel Scavenge收集器的收集策略里就有直接進(jìn)行Major GC的策略選擇過程)。Major GC的速度一般會比Minor GC慢10倍以上僵芹。
2处硬、并發(fā)和并行
這兩個名詞都是并發(fā)編程中的概念,在談?wù)摾占鞯纳舷挛恼Z境中拇派,它們可以解釋如下郁油。
并行(Parallel):指多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態(tài)攀痊。
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時執(zhí)行(但不一定是并行的,可能會交替執(zhí)行)拄显,用戶程序在繼續(xù)運(yùn)行苟径,而垃圾收集程序運(yùn)行于另一個CPU上。
3躬审、吞吐量
吞吐量就是CPU用于運(yùn)行用戶代碼的時間與CPU總消耗時間的比值棘街,即吞吐量 = 運(yùn)行用戶代碼時間 /(運(yùn)行用戶代碼時間 + 垃圾收集時間)。
虛擬機(jī)總共運(yùn)行了100分鐘承边,其中垃圾收集花掉1分鐘遭殉,那吞吐量就是99%。
二博助、垃圾收集器
我發(fā)現(xiàn)有很多垃圾收集器總結(jié)的非常好的博客险污,感覺自己再照著書上寫一遍也沒有什么意義,這里就放幾個鏈接富岳,我發(fā)現(xiàn)的比較好的對于垃圾收集器整理的很完善的文章:
上面的三篇文章中對于垃圾收集器做了很系統(tǒng)的整理蛔糯,我這里再補(bǔ)充一個表格,是常用垃圾收集器組合的表格:
新生代GC | 老年代GC | 說明 | |
---|---|---|---|
組合一 | Serial | Serial Old | Serial和Serial Old都是單線程進(jìn)行GC窖式,特點(diǎn)就是GC時暫停所有應(yīng)用線程蚁飒。 |
組合二 | Serial | CMS+Serial Old | CMS(Concurrent Mark Sweep)是并發(fā)GC,實(shí)現(xiàn)GC線程和應(yīng)用線程并發(fā)工作萝喘,不需要暫停所有應(yīng)用線程淮逻。另外琼懊,當(dāng)CMS進(jìn)行GC失敗時,會自動使用Serial Old策略進(jìn)行GC爬早。 |
組合三 | ParNew | CMS | 使用-XX:+UseParNewGC選項(xiàng)來開啟哼丈。ParNew是Serial的并行版本,可以指定GC線程數(shù)凸椿,默認(rèn)GC線程數(shù)為CPU的數(shù)量削祈。可以使用-XX:ParallelGCThreads選項(xiàng)指定GC的線程數(shù)脑漫。如果指定了選項(xiàng)-XX:+UseConcMarkSweepGC選項(xiàng)髓抑,則新生代默認(rèn)使用ParNew GC策略。 |
組合四 | ParNew | Serial Old | 使用-XX:+UseParNewGC選項(xiàng)來開啟优幸。新生代使用ParNew GC策略吨拍,年老代默認(rèn)使用Serial Old GC策略。 |
組合五 | Parallel Scavenge | Serial Old | Parallel Scavenge策略主要是關(guān)注一個可控的吞吐量:應(yīng)用程序運(yùn)行時間 / (應(yīng)用程序運(yùn)行時間 + GC時間)网杆,可見這會使得CPU的利用率盡可能的高羹饰,適用于后臺持久運(yùn)行的應(yīng)用程序,而不適用于交互較多的應(yīng)用程序碳却。 |
組合六 | Parallel Scavenge | Parallel Old | Parallel Old是Serial Old的并行版本 |
組合七 | G1GC | G1GC | -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC(開啟)XX:MaxGCPauseMillis =50(暫停時間目標(biāo))-XX:GCPauseIntervalMillis =200(暫停間隔目標(biāo))-XX:+G1YoungGenSize=512m(新生代大卸又取)-XX:SurvivorRatio=6(Eden區(qū)和Survivor區(qū)的比例) |
最后在補(bǔ)充一篇文章:
JVM 垃圾回收器工作原理及使用實(shí)例介紹
三、內(nèi)存分配與回收策略
1昼浦、對象優(yōu)先在Eden區(qū)分配
大多數(shù)情況下馍资,對象在新生代的Eden區(qū)中分配。當(dāng)Eden區(qū)沒有足夠空間進(jìn)行分配時关噪,虛擬機(jī)將發(fā)起一次Minor GC鸟蟹。
2、大對象直接進(jìn)入老年代
所謂的大對象是指使兔,需要大量連續(xù)內(nèi)存空間的Java對象建钥,最典型的大對象就是那種很長的字符串以及數(shù)組。
大對象對虛擬機(jī)的內(nèi)存分配來說就是一個壞消息(比遇到一個大對象更加壞的消息就是遇到一群“朝生夕滅”的“短命大對象”虐沥,寫程序的時候應(yīng)當(dāng)避免)熊经,經(jīng)常出現(xiàn)大對象容易導(dǎo)致內(nèi)存還有不少空間時就提前觸發(fā)垃圾收集以獲取足夠的連續(xù)空間來“安置”它們。
虛擬機(jī)提供了一個-XX:PretenureSizeThreshold
參數(shù)置蜀,令大于這個設(shè)置值的對象直接在老年代分配奈搜。這樣做的目的是避免在Eden區(qū)及兩個Survivor區(qū)之間發(fā)生大量的內(nèi)存復(fù)制。
注意:PretenureSizeThreshold
參數(shù)只對Serial和ParNew兩款收集器有效盯荤,Parallel Scavenge收集器不認(rèn)識這個參數(shù)馋吗,Parallel Scavenge收集器一般并不需要設(shè)置。如果遇到必須使用此參數(shù)的場合秋秤,可以考慮ParNew加CMS的收集器組合宏粤。
上面都是書上的內(nèi)容脚翘,并且書上給出了一個例子:
private static final int _1MB = 1024 * 1024;
/**
* VM參數(shù):-verbose:gc(和-XX:+PrintGC具有同樣的作用) -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* -XX:PretenureSizeThreshold=3145728
*/
public static void testPretenureSizeThreshold() {
byte[] allocation;
allocation = new byte[4 * _1MB]; //直接分配在老年代中
}
運(yùn)行結(jié)果:
Heap
def new generation total 9216K, used 671K [0x029d0000, 0x033d0000, 0x033d0000)
eden space 8192K, 8% used [0x029d0000, 0x02a77e98, 0x031d0000)
from space 1024K, 0% used [0x031d0000, 0x031d0000, 0x032d0000)
to space 1024K, 0% used [0x032d0000, 0x032d0000, 0x033d0000)
tenured generation total 10240K, used 4096K [0x033d0000, 0x03dd0000, 0x03dd0000)
the space 10240K, 40% used [0x033d0000, 0x037d0010, 0x037d0200, 0x03dd0000)
compacting perm gen total 12288K, used 2107K [0x03dd0000, 0x049d0000, 0x07dd0000)
the space 12288K, 17% used [0x03dd0000, 0x03fdefd0, 0x03fdf000, 0x049d0000)
No shared spaces configured.
書中給出的解釋:
執(zhí)行代碼中的testPretenureSizeThreshold()方法后,我們看到Eden空間幾乎沒有被使用绍哎,而老年代的10MB空間被使用了40%来农,也就是4MB的allocation對象直接就分配在老年代中,這是因?yàn)镻retenureSizeThreshold被設(shè)置為3MB(就是3145728崇堰,這個參數(shù)不能像-Xmx之類的參數(shù)一樣直接寫3MB)沃于,因此超過3MB的對象都會直接在老年代進(jìn)行分配。
我的一個疑問:
我發(fā)現(xiàn)-XX:PretenureSizeThreshold
這個參數(shù)并沒有默認(rèn)值海诲,那么如果不設(shè)置這個參數(shù)繁莹,虛擬機(jī)會如何做?
我經(jīng)過查找發(fā)現(xiàn)了一篇解決我的疑問的文章:jvm對大對象分配內(nèi)存的特殊處理
原來如果不設(shè)置-XX:PretenureSizeThreshold
參數(shù)的話特幔,當(dāng)對象大小大于Eden區(qū)的時候會直接扔到老年代咨演。我自己也做了一些實(shí)驗(yàn),發(fā)現(xiàn)確實(shí)是這樣蚯斯。
3薄风、長期存活的對象將進(jìn)入老年代
既然虛擬機(jī)采用了分代收集的思想來管理內(nèi)存,那么內(nèi)存回收時就必須能識別哪些對象應(yīng)放在新生代拍嵌,哪些對象應(yīng)放在老年代中遭赂。為了做到這點(diǎn),虛擬機(jī)給每個對象定義了一個對象年齡(Age)計(jì)數(shù)器横辆。如果對象在Eden出生并經(jīng)過第一次Minor GC后仍然存活嵌牺,并且能被Survivor容納的話,將被移動到Survivor空間中龄糊,并且對象年齡設(shè)為1。對象在Survivor區(qū)中每“熬過”一次Minor GC募疮,年齡就增加1歲炫惩,當(dāng)它的年齡增加到一定程度(默認(rèn)為15歲),就將會被晉升到老年代中阿浓。對象晉升老年代的年齡閾值他嚷,可以通過參數(shù)-XX:MaxTenuringThreshold
設(shè)置。
4芭毙、動態(tài)對象年齡判定
為了能更好地適應(yīng)不同程序的內(nèi)存狀況筋蓖,虛擬機(jī)并不是永遠(yuǎn)地要求對象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半退敦,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代粘咖,無須等到MaxTenuringThreshold中要求的年齡。
5侈百、空間分配擔(dān)保
在發(fā)生Minor GC之前瓮下,虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間翰铡,如果這個條件成立,那么Minor GC可以確保是安全的讽坏。如果不成立锭魔,則虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗。如果允許路呜,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小迷捧,如果大于,將嘗試著進(jìn)行一次Minor GC胀葱,盡管這次Minor GC是有風(fēng)險的漠秋;如果小于,或者HandlePromotionFailure設(shè)置不允許冒險巡社,那這時也要改為進(jìn)行一次Full GC膛堤。
前面提到過,新生代使用復(fù)制收集算法晌该,但為了內(nèi)存利用率肥荔,只使用其中一個Survivor空間來作為輪換備份,因此當(dāng)出現(xiàn)大量對象在Minor GC后仍然存活的情況(最極端的情況就是內(nèi)存回收后新生代中所有對象都存活)朝群,就需要老年代進(jìn)行分配擔(dān)保燕耿,把Survivor無法容納的對象直接進(jìn)入老年代。與生活中的貸款擔(dān)保類似姜胖,老年代要進(jìn)行這樣的擔(dān)保誉帅,前提是老年代本身還有容納這些對象的剩余空間,一共有多少對象會活下來在實(shí)際完成內(nèi)存回收之前是無法明確知道的右莱,所以只好取之前每一次回收晉升到老年代對象容量的平均大小值作為經(jīng)驗(yàn)值蚜锨,與老年代的剩余空間進(jìn)行比較,決定是否進(jìn)行Full GC來讓老年代騰出更多空間慢蜓。
取平均值進(jìn)行比較其實(shí)仍然是一種動態(tài)概率的手段亚再,也就是說,如果某次Minor GC存活后的對象突增晨抡,遠(yuǎn)遠(yuǎn)高于平均值的話氛悬,依然會導(dǎo)致?lián)J。℉andle Promotion Failure)耘柱。如果出現(xiàn)了HandlePromotionFailure失敗如捅,那就只好在失敗后重新發(fā)起一次Full GC。雖然擔(dān)保失敗時繞的圈子是最大的调煎,但大部分情況下都還是會將HandlePromotionFailure開關(guān)打開镜遣,避免Full GC過于頻繁。
注意:在JDK 6 Update 24版本之后士袄,HandlePromotionFailure參數(shù)就沒有作用了烈涮,因?yàn)橐?guī)則變?yōu)榱耍褐灰夏甏倪B續(xù)空間大于新生代對象的總大小或者歷次晉升的對象的平均大小朴肺,就進(jìn)行Minor GC 否則進(jìn)行Full GC。