JVM系列之GC
談到JVM懦铺,大家都知道GC(Garbage Collection),GC這個(gè)話題說淺了就一句話--JVM自動(dòng)垃圾收集,說深了就無止盡了支鸡,回收算法冬念,各種收集器,gc類型,gc觸發(fā)點(diǎn)....等等
鑒于作者才學(xué)疏淺牧挣,這篇博文還是準(zhǔn)備用通熟易懂的話把作者自己對GC這一塊的理解做陳述急前,概要如下:
文章結(jié)構(gòu)
- 哪些內(nèi)存需要回收(Which)
- 各種GC的觸發(fā)時(shí)機(jī)(When)
- 如何回收(How)
3.1 回收算法
3.2 HotSpot的具體實(shí)現(xiàn)-各種收集器- GC日志
1. 哪些內(nèi)存需要回收(Which)
大多數(shù)沒干過C或者C++的Javaer是幸福的,因?yàn)闆]有體會(huì)過那種自己new delete內(nèi)存的感覺瀑构,創(chuàng)建對象就是new,不管內(nèi)存的回收問題裆针。其實(shí)我們的內(nèi)存是JVM的GC機(jī)制來幫我們回收的。那么問題來了。到底哪些內(nèi)存需要回收呢据块?
答案:可達(dá)性分析算法,說白了码邻,就是JVM預(yù)先確定一組GC roots引用變量折剃,如Student stu =new Student();這個(gè)stu就可以作為GC roots,當(dāng)進(jìn)行垃圾回收時(shí)另假,JVM通過GC Roots找到能夠引用到的所有活對象,然后把剩下的對象標(biāo)記為"無用",即可回收狀態(tài)!
能夠作為GC roots的引用如下:
- 所有Java線程當(dāng)前活躍的棧幀里指向GC堆里的對象的引用怕犁;換句話說边篮,當(dāng)前 所有正在被調(diào)用的方法的引用類型的參數(shù)/局部變量/臨時(shí)值。
- VM的一些靜態(tài)數(shù)據(jù)結(jié)構(gòu)里指向GC堆里的對象的引用奏甫,例如說HotSpot VM里的Universe里有很多這樣的引用戈轿。
- JNI handles,包括global handles和local handles(看情況)所有當(dāng)前被加載的Java類(看情況)Java類的引用類型靜態(tài)變量(看情況)Java類的運(yùn)行時(shí)常量池里的引用類型常量(String或Class類型)(看情況)String常量池(StringTable)里的引用
2. 各種GC的觸發(fā)時(shí)機(jī)(When)
2.1 GC類型
說到GC類型阵子,就更有意思了思杯,為什么呢,因?yàn)闃I(yè)界沒有統(tǒng)一的嚴(yán)格意義上的界限挠进,也沒有嚴(yán)格意義上的GC類型色乾,都是左邊一個(gè)教授一套名字,右邊一個(gè)作者一套名字领突。為什么會(huì)有這個(gè)情況呢暖璧,因?yàn)镚C類型是和收集器有關(guān)的,不同的收集器會(huì)有自己獨(dú)特的一些收集類型君旦。所以作者在這里引用R大關(guān)于GC類型的介紹澎办,作者覺得還是比較妥當(dāng)準(zhǔn)確的。如下:
- Partial GC:并不收集整個(gè)GC堆的模式
- Young GC(Minor GC):只收集young gen的GC
- Old GC:只收集old gen的GC金砍。只有CMS的concurrent collection是這個(gè)模式
- Mixed GC:收集整個(gè)young gen以及部分old gen的GC局蚀。只有G1有這個(gè)模式
- Full GC(Major GC):收集整個(gè)堆,包括young gen恕稠、old gen至会、perm gen(如果存在的話)等所有部分的模式。
2.2 觸發(fā)時(shí)機(jī)
上面大家也看到了谱俭,GC類型分分類是和收集器有關(guān)的奉件,那么當(dāng)然了,對于不同的收集器昆著,GC觸發(fā)時(shí)機(jī)也是不一樣的凯傲,作者就針對默認(rèn)的serial GC來說:
- young GC:當(dāng)young gen中的eden區(qū)分配滿的時(shí)候觸發(fā)业崖。注意young GC中有部分存活對象會(huì)晉升到old gen,所以young GC后old gen的占用量通常會(huì)有所升高。
- full GC:當(dāng)準(zhǔn)備要觸發(fā)一次young GC時(shí)收捣,如果發(fā)現(xiàn)統(tǒng)計(jì)數(shù)據(jù)說之前young GC的平均晉升大小比目前old gen剩余的空間大困食,則不會(huì)觸發(fā)young GC而是轉(zhuǎn)為觸發(fā)full GC(因?yàn)镠otSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都會(huì)同時(shí)收集整個(gè)GC堆塘匣,包括young gen,所以不需要事先觸發(fā)一次單獨(dú)的young GC)巷帝;或者忌卤,如果有perm gen的話,要在perm gen分配空間但已經(jīng)沒有足夠空間時(shí)楞泼,也要觸發(fā)一次full GC驰徊;或者System.gc()、heap dump帶GC堕阔,默認(rèn)也是觸發(fā)full GC棍厂。
3. 如何回收(How)
3.1 回收算法
由于網(wǎng)上已經(jīng)擁有非常多的優(yōu)秀博文來詳細(xì)介紹關(guān)于回收算法這塊,所以這塊作者將引用其他博客的介紹并加上自己的一些描述:
3.1.1 標(biāo)記清除算法(Mark-Sweep)
3.1.2復(fù)制算法(Coping)(絕大部分收集器的新生代使用的算法)
復(fù)制算法在JVM新生代垃圾回收中的運(yùn)用:
Eden:From:TO =8:1:1
由于新生代中90%的對象都是"朝生夕死"超陆,采用復(fù)制算法是比較合理的牺弹,首先只移動(dòng)了存活下來的對象(比較少數(shù)),其次,內(nèi)存在移動(dòng)到To區(qū)域后是有順序的时呀,不存在內(nèi)存碎片张漂。
值得一提的是,假如在一次MinorGC時(shí),Eden中存活的對象+From中存活的對象>To的剩余空間退唠,則會(huì)通過擔(dān)保機(jī)制將對象直接轉(zhuǎn)移到Old gen ,如果Old gen的內(nèi)存空間也不夠鹃锈,則進(jìn)行一次Full gc .
當(dāng)對象的年齡到達(dá)15歲時(shí)會(huì)轉(zhuǎn)移到Old gen (可通過參數(shù)配置,一般不建議更改瞧预。)
3.1.3標(biāo)記-整理算法(Mark-Compact):
由于Old gen 的大部分對象都是年齡很大的對象屎债,所以存活率比較高,采用復(fù)制算法肯定是行不通的(較多的對象復(fù)制操作)垢油,所以才大部分收集器的old gen采用 Mark-Compact算法盆驹,避免了空間碎片。
3.1.4三種算法比較:
稍微解釋一下常見的關(guān)于GC時(shí)間的問題:
為什么FGC的時(shí)間比MinorGC長很多滩愁?
答:FGC進(jìn)行了old gen的gc躯喇,由于算法上采用Mark-Sweep或者M(jìn)ark-Compact,進(jìn)行了很多對象(老年代存活率很低)的移動(dòng)硝枉,當(dāng)然很耗時(shí)了廉丽!其實(shí)就是空間換時(shí)間,時(shí)間換空間的問題妻味。
3.2 HotSpot的具體實(shí)現(xiàn)-各種收集器
關(guān)于收集器這塊正压,由于本人也是JVM初學(xué)者,加上很少有在生產(chǎn)環(huán)境做收集器參數(shù)調(diào)整责球,搭配使用的機(jī)會(huì)焦履。所以可以說對于一些HotSpot收集器只是停留在
書籍與博文層次
4 GC日志
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:/Users/zdy/Desktop/dump/gclog.txt
當(dāng)服務(wù)器出現(xiàn)卡頓比較頻繁時(shí)拓劝,嘗試看下自己的GC日志,注意Full gc 頻率嘉裤。
最后郑临,稍微說一下作者的心得:
- 如果是服務(wù)器一次卡頓時(shí)間比較長,一般是full gc時(shí)間過長,而應(yīng)用最求的是卡頓(STW)時(shí)間短屑宠,可以接受多次卡頓厢洞,那么可以考慮調(diào)整加大young gen的比例,和提高進(jìn)入老年代的年齡(默認(rèn)15歲)來盡量避免FGC.
- 選擇合適的收集器很重要侨把。要根據(jù)應(yīng)用的特點(diǎn)犀变。是追求吞吐量妹孙,還是追求最小停頓秋柄。
- 經(jīng)常對照gc日志觀察現(xiàn)實(shí)的情況,如多長時(shí)間一卡頓蠢正,多久一卡頓骇笔,然后來調(diào)整自己的收集器或者相關(guān)的內(nèi)存比例來達(dá)到自己想要的效果。
- 在有限的物理資源條件下嚣崭,要避免讓用戶接受過多的STW笨触,可以考慮在半夜自動(dòng)進(jìn)行g(shù)c(System.gc()),雖然不一定生效,但可以觀察下效果雹舀。多數(shù)情況下是會(huì)觸發(fā)full gc 的芦劣。
- 大多數(shù)應(yīng)用是可以接受頻繁的mgc,但卻不能接受full gc 的長時(shí)間卡頓说榆,所以在觀察gc日志時(shí)一定要注意自己full gc的頻率和觸發(fā)條件(是由于內(nèi)存擔(dān)保虚吟,還是年齡到了,還是TO內(nèi)存太小签财,導(dǎo)致每次都fgc.).
如果您覺得這篇文章對你有幫助串慰,請點(diǎn)贊或者喜歡,讓更多的人看到唱蒸!祝你每天開心愉快邦鲫!