前言
目前忠怖,互聯(lián)網(wǎng)上 Java 的 GC 資料要么是主要講解理論腹缩,要么就是針對單一場景的 GC 問題進(jìn)行了剖析欲鹏,對整個(gè)體系總結(jié)的資料少之又少机久。前車之鑒,后事之師赔嚎,我們搜集了內(nèi)部各種 GC 問題的分析文章膘盖,并結(jié)合個(gè)人的理解做了一些總結(jié),希望能起到“拋磚引玉”的作用尤误。
關(guān)于GC
GC技術(shù)是JAVA語言用來進(jìn)行內(nèi)存自動(dòng)管理的侠畔,避免了手動(dòng)管理帶來的懸掛指針(Dangling Pointer)的問題,大大提升了開發(fā)的效率损晤。在GC技術(shù)發(fā)展到現(xiàn)在软棺,都是基于三種基礎(chǔ)GC算法的組合或應(yīng)用。
我們有時(shí)候在排查問題的時(shí)候尤勋,比較RT過高喘落、GC耗時(shí)過長、CPU消耗過高斥黑,或多或少都會(huì)伴隨著GC的產(chǎn)生揖盘,我們在日常工作中眉厨,需要去分析與判斷锌奴,產(chǎn)生這些問題的根因是什么?如何才能避免這些問題的產(chǎn)生憾股。
我們系統(tǒng)普遍使用的CMS GC的算法,我們希望通過我們對我們歷史問題的分析鹿蜀,一起來提升GC問題的能力。
1. GC基礎(chǔ)知識(shí)
現(xiàn)在我們公司JAVA 8版本服球,所有以下的討論都是基于這個(gè)前提進(jìn)行討論的茴恰。
1.1. JVM內(nèi)存
GC 主要工作在 Heap 區(qū)和 MetaSpace 區(qū)(上圖藍(lán)色部分),在 Direct Memory 中斩熊,如果使用的是 DirectByteBuffer往枣,那么在分配內(nèi)存不夠時(shí)則是 GC 通過 Cleaner#clean 間接管理。
1.2. 內(nèi)存分配模式
簡單的將粉渠,就是我們new一個(gè)對象的時(shí)候分冈,堆內(nèi)存的分配方式有兩種模式。
- 空閑鏈表(free list):通過額外的存儲(chǔ)記錄空閑的地址霸株,將隨機(jī) IO 變?yōu)轫樞?IO雕沉,但帶來了額外的空間消耗。(ps:CMS)
- 碰撞指針(bump pointer):通過一個(gè)指針作為分界點(diǎn)去件,需要分配內(nèi)存時(shí)坡椒,僅需把指針往空閑的一端移動(dòng)與對象大小相等的距離扰路。(ps:Serial、ParNew)
1.3. 垃圾收集
什么是垃圾倔叼?在程序運(yùn)行過程中已經(jīng)使用完畢汗唱,且之后不需要在被使用的對象我們稱為垃圾。
1.3.1. 垃圾識(shí)別
- Reference Count(引用計(jì)數(shù)):對每個(gè)對象的引用進(jìn)行計(jì)數(shù)丈攒,每當(dāng)有一個(gè)地方引用它時(shí)計(jì)數(shù)器 +1渡嚣、引用失效則 -1,引用的計(jì)數(shù)放到對象頭中肥印,當(dāng)引用計(jì)數(shù)器為0的時(shí)候识椰,認(rèn)為對象已經(jīng)失效,即為垃圾深碱。之前有些文章會(huì)說腹鹉,引用計(jì)數(shù)無法解決循環(huán)引用的問題,事實(shí)上已經(jīng)使用Recycler算法解決了》蠊瑁現(xiàn)在現(xiàn)在功咒,高并發(fā)的場景下,引用計(jì)數(shù)變更也要進(jìn)行昂貴的同步操作绞蹦,性能較低力奋,現(xiàn)在的語言不使用。
-
Root Searching(根節(jié)點(diǎn)搜索法):從Root開始進(jìn)行對象搜索幽七,可以被搜索到的對象為可達(dá)對象景殷,多次標(biāo)記才能確定一個(gè)對象是否存活還是死亡。多次查詢之后澡屡,所有沒有被查詢到的對象猿挚,都標(biāo)識(shí)為垃圾,可以被回收驶鹉。這個(gè)是現(xiàn)在java虛擬器中采用的主流的垃圾識(shí)別方案
Root Searching
1.3.2. GC算法
我們已經(jīng)知道如何去識(shí)別虛擬機(jī)產(chǎn)生的垃圾绩蜻,我們就需要解決如何更快速的識(shí)別到并且進(jìn)行清除。
-
標(biāo)記-清除(Mark-Sweep):看名字就很好理解室埋,主要分兩個(gè)階段办绝,第一階段,通過Root Searching的方式姚淆,找出所有可達(dá)有效對象孕蝉,并且進(jìn)行標(biāo)記;第二階段肉盹,將沒有標(biāo)記的對象進(jìn)行清除昔驱,這個(gè)過程對象不發(fā)生移動(dòng),不進(jìn)行內(nèi)存的整理上忍。
GC-MS清除 標(biāo)記-整理(Mark-Compact):第一階段與Mark-Sweep一樣骤肛;第二階段纳本,將可達(dá)有效對象壓縮進(jìn)行整理,然后清除不可達(dá)對象腋颠。
-
復(fù)制(Copying):將內(nèi)存空間分為兩塊繁成,同一時(shí)間只會(huì)使用其中一個(gè),每次進(jìn)行回收的時(shí)候淑玫,將可達(dá)對象復(fù)制移動(dòng)到另一個(gè)半?yún)^(qū)巾腕,并且清空內(nèi)存里面所有的對象,并且將兩個(gè)內(nèi)存分區(qū)角色進(jìn)行切換絮蒿。
Copying
GC算法 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|
標(biāo)記-清除(Mark-Sweep) | 性能比較好尊搬,不用移動(dòng)空間 | 會(huì)有碎片,特別對象比較大的情況 |
標(biāo)記-整理(Mark-Compact) | 沒有碎片土涝,空間利用率高 | 性能比較低 |
復(fù)制(Copying) | 性能最好 | 空間利用率低佛寿,有大量的空間浪費(fèi) |
1.4. 收集器
收集器主要分兩個(gè)分代收集器與分區(qū)收集器
1.5. 常用的分析工具
1.5.1. 命令行
- 標(biāo)準(zhǔn)終端類:jps、jinfo但壮、jstat冀泻、jstack、jmap
- 功能整合類:jcmd蜡饵、vjtools弹渔、arthas、greys
1.5.2. 可視化
- JConsole溯祸、JVisualvm肢专、HA、GCHisto您没、GCViewer鸟召、MAT、JProfiler
我們系統(tǒng)會(huì)使用阿里開源的arthas氨鹏,以及JProfiler。
總結(jié)
本章我們將GC的基礎(chǔ)做了一個(gè)了解压状,這個(gè)只是一個(gè)皮毛還有很多東西可以深入的去了解仆抵,比如想去了解內(nèi)存是如何移動(dòng)的,各個(gè)算法是如何去實(shí)現(xiàn)的种冬,有興趣的都可以去了解下镣丑。
原文《Java中常見的CMS GC問題分析與解決》