原文:https://blog.csdn.net/yhyr_ycy/article/details/52566105
GC,即就是Java垃圾回收機(jī)制唠帝。目前主流的JVM(HotSpot)采用的是分代收集算法惧眠。與C++不同的是疹瘦,Java采用的是類似于樹(shù)形結(jié)構(gòu)的可達(dá)性分析法來(lái)判斷對(duì)象是否還存在引用霜定。即:從gcroot開(kāi)始嗤放,把所有可以搜索得到的對(duì)象標(biāo)記為存活對(duì)象。
要準(zhǔn)確理解Java的垃圾回收機(jī)制壁酬,就要從:“什么時(shí)候”次酌,“對(duì)什么東西”恨课,“做了什么”三個(gè)方面來(lái)具體分析。
第一:“什么時(shí)候”即就是GC觸發(fā)的條件岳服。GC觸發(fā)的條件有兩種剂公。(1)程序調(diào)用System.gc時(shí)可以觸發(fā);(2)系統(tǒng)自身來(lái)決定GC觸發(fā)的時(shí)機(jī)吊宋。
系統(tǒng)判斷GC觸發(fā)的依據(jù):根據(jù)Eden區(qū)和From?Space區(qū)的內(nèi)存大小來(lái)決定纲辽。當(dāng)內(nèi)存大小不足時(shí),則會(huì)啟動(dòng)GC線程并停止應(yīng)用線程璃搜。
第二:“對(duì)什么東西”籠統(tǒng)的認(rèn)為是Java對(duì)象并沒(méi)有錯(cuò)拖吼。但是準(zhǔn)確來(lái)講,GC操作的對(duì)象分為:通過(guò)可達(dá)性分析法無(wú)法搜索到的對(duì)象和可以搜索到的對(duì)象这吻。對(duì)于搜索不到的方法進(jìn)行標(biāo)記吊档。
第三:“做了什么”最淺顯的理解為釋放對(duì)象。但是從GC的底層機(jī)制可以看出唾糯,對(duì)于可以搜索到的對(duì)象進(jìn)行復(fù)制操作怠硼,對(duì)于搜索不到的對(duì)象,調(diào)用finalize()方法進(jìn)行釋放移怯。
具體過(guò)程:當(dāng)GC線程啟動(dòng)時(shí)香璃,會(huì)通過(guò)可達(dá)性分析法把Eden區(qū)和From?Space區(qū)的存活對(duì)象復(fù)制到To Space區(qū),然后把Eden Space和From?Space區(qū)的對(duì)象釋放掉舟误。當(dāng)GC輪訓(xùn)掃描To?Space區(qū)一定次數(shù)后葡秒,把依然存活的對(duì)象復(fù)制到老年代,然后釋放To Space區(qū)的對(duì)象脐帝。
對(duì)于用可達(dá)性分析法搜索不到的對(duì)象同云,GC并不一定會(huì)回收該對(duì)象。要完全回收一個(gè)對(duì)象堵腹,至少需要經(jīng)過(guò)兩次標(biāo)記的過(guò)程炸站。
第一次標(biāo)記:對(duì)于一個(gè)沒(méi)有其他引用的對(duì)象,篩選該對(duì)象是否有必要執(zhí)行finalize()方法疚顷,如果沒(méi)有執(zhí)行必要旱易,則意味可直接回收。(篩選依據(jù):是否復(fù)寫(xiě)或執(zhí)行過(guò)finalize()方法腿堤;因?yàn)閒inalize方法只能被執(zhí)行一次)阀坏。
第二次標(biāo)記:如果被篩選判定位有必要執(zhí)行,則會(huì)放入FQueue隊(duì)列笆檀,并自動(dòng)創(chuàng)建一個(gè)低優(yōu)先級(jí)的finalize線程來(lái)執(zhí)行釋放操作忌堂。如果在一個(gè)對(duì)象釋放前被其他對(duì)象引用,則該對(duì)象會(huì)被移除FQueue隊(duì)列酗洒。
通過(guò)上面的GC過(guò)程不難看出士修,Java堆中的年輕代和老年代采用了不同的回收算法枷遂。
年輕代采用了復(fù)制法,而老年代采用了標(biāo)記-整理法棋嘲。
具體各種回收算法的詳解參考:http://www.cnblogs.com/dolphin0520/p/3783345.html
程序計(jì)數(shù)器:線程私有酒唉。是一塊較小的內(nèi)存,是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器沸移。是Java虛擬機(jī)規(guī)范中唯一沒(méi)有規(guī)定OOM(OutOfMemoryError)的區(qū)域痪伦。
Java棧:線程私有。生命周期和線程相同雹锣。是Java方法執(zhí)行的內(nèi)存模型网沾。執(zhí)行每個(gè)方法都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量和操作數(shù)(對(duì)象引用)笆制。局部變量所需要的內(nèi)存空間大小在編譯期間完成分配绅这。所以棧幀的大小不會(huì)改變。存在兩種異常情況:若線程請(qǐng)求深度大于棧的深度在辆,拋StackOverflowError证薇。若棧在動(dòng)態(tài)擴(kuò)展時(shí)無(wú)法請(qǐng)求足夠內(nèi)存馏谨,拋OOM坦康。
本地方法棧:線程私有。與Java棧類似缎患,但是不是為Java方法(字節(jié)碼)服務(wù)鸦概,而是為本地非Java方法服務(wù)箩张。也會(huì)拋StackOverflowError和OOM。
堆:線程共享窗市。虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建先慷。存放對(duì)象實(shí)力和數(shù)組。所占內(nèi)存最大咨察。分為新生代(Young區(qū))论熙,老年代(Old區(qū))。新生代分Eden區(qū)摄狱,Servior區(qū)脓诡。Servior區(qū)又分為From space區(qū)和To Space區(qū)。Eden區(qū)和Servior區(qū)的內(nèi)存比為8:1媒役。 當(dāng)擴(kuò)展內(nèi)存大于可用內(nèi)存祝谚,拋OOM。
方法區(qū):線程共享酣衷。用于存儲(chǔ)已被虛擬機(jī)加載的類信息交惯、常量、靜態(tài)變量等數(shù)據(jù)。又稱為非堆(Non – Heap)商玫。方法區(qū)又稱“永久代”(Java8后為元空間)箕憾。GC很少在這個(gè)區(qū)域進(jìn)行牡借,但不代表不會(huì)回收拳昌。這個(gè)區(qū)域回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載。當(dāng)內(nèi)存申請(qǐng)大于實(shí)際可用內(nèi)存钠龙,拋OOM炬藤。
Minor GC ,F(xiàn)ull GC 觸發(fā)條件
Minor GC觸發(fā)條件:當(dāng)Eden區(qū)滿時(shí)碴里,觸發(fā)Minor GC沈矿。
Full GC觸發(fā)條件:
調(diào)用System.gc()時(shí),系統(tǒng)建議執(zhí)行Full GC咬腋,但是不必然執(zhí)行
老年代空間不足
方法區(qū)空間不足
通過(guò)Minor GC后進(jìn)入老年代的平均大小大于老年代的可用內(nèi)存
由Eden區(qū)羹膳、From Space區(qū)向To Space區(qū)復(fù)制時(shí),對(duì)象大小大于To Space可用內(nèi)存根竿,則把該對(duì)象轉(zhuǎn)存到老年代陵像,且老年代的可用內(nèi)存小于該對(duì)象大小