了解Java中的垃圾回收(GC)的工作原理有什么好處侈贷?滿足軟件工程師的求知欲是一個正當?shù)睦碛桑粌H如此加匈,了解GC的工作原理也可以幫助您編寫更好的Java應(yīng)用程序洞辣。
這是我個人的非常主觀的看法,但是我相信精通GC的人往往是更好的Java開發(fā)人員码俩。如果您對GC流程感興趣度帮,則意味著您具有開發(fā)特定大小的應(yīng)用程序的經(jīng)驗。如果您仔細地考慮過選擇正確的GC算法稿存,則意味著您完全了解所開發(fā)應(yīng)用程序的功能笨篷。當然瞳秽,對于優(yōu)秀的開發(fā)人員來說,這可能不是通用的標準率翅。但是练俐,當我說理解GC是成為一名出色的Java開發(fā)人員的要求時,很少有人會反對安聘。?
這是“?成為Java GC專家?”系列文章中的第一篇痰洒。這次我將介紹GC,在下一篇文章中浴韭,我將討論分析特定的GC狀態(tài)和調(diào)優(yōu)示例。
在學(xué)習(xí)GC之前脯宿,您應(yīng)該了解一個術(shù)語念颈。術(shù)語是STW(?Stop The World?)。意味著JVM需要停止運行應(yīng)用程序以執(zhí)行GC连霉。這意味著除GC所需的線程以外的所有線程都將停止其任務(wù)榴芳,無法處理外部響應(yīng)。跺撼。被中斷的任務(wù)將僅在GC任務(wù)完成后才能恢復(fù)窟感。無論您選擇哪種GC算法,STW都會發(fā)生歉井。GC調(diào)優(yōu)通常意味著減少STW時間柿祈。
分代垃圾收集?
Java沒有顯式釋放內(nèi)存的操作。有人將相關(guān)對象設(shè)置為null或使用System.gc()方法顯式釋放內(nèi)存哩至,這是不合適的躏嚎。調(diào)用System.gc()方法將觸發(fā)垃圾回收,極大地影響系統(tǒng)性能菩貌。將對象設(shè)為null其實在大多種場景下沒有任何意義卢佣。
在Java中,由于開發(fā)人員未明確刪除程序代碼中的內(nèi)存箭阶,因此垃圾收集器會找到不再需要的(垃圾)對象并將其刪除虚茶。該垃圾收集器是基于以下兩個假設(shè)創(chuàng)建的。
1仇参、大多數(shù)新創(chuàng)建的對象生命周期都很短
2嘹叫、從老對象到新對象的引用數(shù)量很少。
這些假設(shè)稱為分代假設(shè)冈敛〈Γ基于此,在HotSpot VM實現(xiàn)中將其從物理上分為年輕代和老年代抓谴。
年輕代(Yong Gen):大多數(shù)新創(chuàng)建的對象都位于此處暮蹂。由于大多數(shù)對象很快變得無法訪問寞缝,因此許多對象是在年輕一代中創(chuàng)建的,然后死亡了仰泻。當回收年輕代對象時荆陆,我們稱之為“ Minor?GC?”。
年老代(Old Gen):從年輕代幸存下來的對象復(fù)制到此處集侯。它通常比年輕一代大被啼。同時一些大對象也在此分配。由于它的大小較大棠枉,因此與年輕一代相比浓体,GC發(fā)生的頻率會降低,耗時會更久辈讶。當回收老年代對象時命浴,我們說發(fā)生了“? Major?GC?”(或“ Full?GC?”)。
見下圖:
圖1:GC區(qū)域和數(shù)據(jù)流贱除。
上表中的永久代也稱為“?方法區(qū)域?”生闲,它存儲類或長量字符串。在該區(qū)域可能會發(fā)生GC月幌,此處發(fā)生的GC仍被視為Major GC(Full GC)碍讯。?
有人可能會懷疑:
如果老一代的對象需要引用年輕一代的對象怎么辦?
為了處理這些情況扯躺,在年老代中有一個稱為“?卡片表?”的東西捉兴,它是一個512字節(jié)的塊。每當年老代中的對象引用年輕一代中的對象時缅帘,該對象都會記錄在此表中轴术。當為年輕代執(zhí)行GC時,僅搜索此卡表以確定它是否適用于GC钦无,而不是檢查年老代中所有對象的引用逗栽。
圖2:卡片表結(jié)構(gòu)。
年輕代的組成
年輕代是第一次創(chuàng)建對象的地方失暂。年輕一代分為3個空間彼宠。?
一個 Eden區(qū)
兩個 Survivor(From,To)區(qū)
總共有3個區(qū)弟塞,其中兩個是幸存者區(qū)凭峡。每個區(qū)的執(zhí)行過程順序如下:
1、大多數(shù)新創(chuàng)建的對象位于Eden區(qū)中决记。
2摧冀、在Eden區(qū)中進行一次GC之后,將幸存的對象移動到其中一個Survivor區(qū)。?
3索昂、在Eden區(qū)進行GC之后建车,將這些對象堆積到Survivor區(qū)中,該區(qū)中已經(jīng)存在其他幸存的物體椒惨。
4缤至、一旦Survivor區(qū)已滿,就將幸存對象移動到另一個Survivor區(qū)康谆。然后领斥,已滿的Survivor區(qū)將更改為完全沒有數(shù)據(jù)的狀態(tài)。
在這些步驟中幸存下來的對象(已重復(fù)多次)被移到了年老代沃暗。
通過檢查這些步驟可以看到月洛,Survivor區(qū)之一必須保持為空。
下表顯示了通過次要GC收集到舊數(shù)據(jù)中的數(shù)據(jù)的過程:
圖3:GC前和后孽锥。
在HotSpot VM中膊存,使用兩種技術(shù)來加快內(nèi)存分配。一個稱為“?碰撞指針?”忱叭,另一個稱為“?TLAB(線程本地分配緩沖區(qū))”。?
指針碰撞技術(shù)跟蹤分配給Eden區(qū)的最后一個對象今艺。該物體將位于Eden區(qū)的頂部韵丑。并且如果之后創(chuàng)建了一個對象,則僅檢查該對象的大小是否適合Eden空間虚缎。如果上述對象看起來正確撵彻,它將被放置在Eden空間中,新對象將位于頂部实牡。因此陌僵,在創(chuàng)建新對象時,僅需要檢查最后添加的對象创坞,從而可以更快地分配內(nèi)存碗短。但是,如果我們考慮多線程環(huán)境题涨,則情況就不同了偎谁。為了將多個線程使用的對象保存在Eden空間中以確保線程安全,將發(fā)生不可避免的鎖定纲堵,并且由于鎖定競爭而導(dǎo)致性能下降巡雨。TLAB?是HotSpot VM中解決此問題的方法。這允許每個線程在其Eden空間中有一小部分與其對應(yīng)的份額相對應(yīng)席函。由于每個線程只能訪問自己的TLAB铐望,因此即使是“撞球指針”技術(shù)也可以在沒有鎖的情況下分配內(nèi)存。?
年老代GC
當堆空間不夠用時會發(fā)生年老代GC。年老代GC的執(zhí)行過程根據(jù)GC策略類型的不同區(qū)別很大正蛙。對于JDK 7督弓,8,有5種GC類型跟畅。?
1咽筋、Serial GC - 串行GC
2、Parallel GC - 并行GC
3徊件、Parallel Old GC (Parallel Compacting GC)?
4奸攻、Concurrent Mark & Sweep GC ?(or "CMS")?
5、Garbage First (G1) GC
其中虱痕,Serial GC不得在運行服務(wù)器上使用睹耐。當臺式計算機上只有一個CPU內(nèi)核時,就會創(chuàng)建此GC類型部翘。使用此串行GC將大大降低應(yīng)用程序性能硝训。?
現(xiàn)在讓我們了解每種GC類型。
串行GC(-XX:+ UseSerialGC)
年輕一代中的GC使用我們在上一段中介紹的類型新思。老一代的GC使用一種稱為“?mark-sweep-compact?”?的算法窖梁。
標記-清除-整理算法會將清理后的堆的整理成連續(xù)的內(nèi)存空間。
串行GC適用于較小的內(nèi)存和少量的CPU內(nèi)核夹囚。
并行GC(-XX:+ UseParallelGC)
圖4:串行GC和并行GC之間的區(qū)別纵刘。
從圖片中,您可以輕松地看到串行GC和并行GC之間的區(qū)別荸哟。串行GC僅使用一個線程來處理GC假哎,而并行GC使用多個線程來處理GC,因此速度更快鞍历。當有足夠的內(nèi)存和大量內(nèi)核時舵抹,此GC很有用。也稱為“?吞吐量GC”劣砍。?
并行舊GC(-XX:+ UseParallelOldGC)
從JDK5開始支持惧蛹。與并行GC相比,唯一的區(qū)別是年老代的GC算法相對復(fù)雜秆剪。它經(jīng)歷了三個步驟:標記–摘要–整理赊淑。
CMS GC(-XX:+ UseConcMarkSweepGC)
圖5:串行GC和CMS GC
從圖片中可以看到,CMS比到目前為止我所解釋的任何其他GC類型都要復(fù)雜得多仅讽。早期的初始標記步驟很簡單陶缺。在最接近類加載器的對象中搜索尚存的對象。因此洁灵,暫停時間很短饱岸。在并發(fā)標記步驟中掺出,跟蹤并檢查了剛被確認的存活對象所引用的對象。此步驟的不同之處在于苫费,它是在同時處理其他線程的同時進行的汤锨。在重新標記步驟中,將檢查并發(fā)標記步驟中新添加或停止引用的對象百框。最后闲礼,在并發(fā)收集中步驟,將執(zhí)行垃圾收集過程铐维。在仍在處理其他線程時執(zhí)行垃圾回收柬泽。由于以這種方式執(zhí)行此GC類型,因此GC的暫停時間非常短嫁蛇。CMS GC也稱為低延遲GC锨并,在所有應(yīng)用程序的響應(yīng)時間至關(guān)重要時使用。?
CMS的一大有點是縮短了STW時間睬棚,但它也具有以下缺點:
1第煮、它比其他GC類型使用更多的內(nèi)存和CPU。
2抑党、默認情況下不提供壓縮步驟包警,容易導(dǎo)致內(nèi)存碎片
使用此類型之前,您需要仔細檢查底靠。另外揽趾,如果由于內(nèi)存碎片過多而需要執(zhí)行壓縮任務(wù),那么STW時間可能比任何其他GC類型都要長苛骨。您需要檢查壓縮任務(wù)執(zhí)行的頻率和時間。
G1 GC
最后苟呐,讓我們了解G1 GC痒芝。?
圖6:G1 GC的布局。
如果您想了解G1 GC牵素,請忘記所有有關(guān)年輕一代和老一代的知識严衬。如您在圖片中看到的,將一個對象分配給每個Region笆呆,然后執(zhí)行GC请琳。G1 GC是基于停頓時間為目標的回收算法,以最大程度的減少STW