本篇文章為大家展示了Java中JVM內(nèi)存布局的GC原理是怎樣的,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括普定網(wǎng)站建設(shè)、普定網(wǎng)站制作、普定網(wǎng)頁(yè)制作以及普定網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,普定網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到普定省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
剛開(kāi)始時(shí),對(duì)象分配在 eden 區(qū),s0(即:from)及 s1(即:to)區(qū),幾乎是空著。
隨著應(yīng)用的運(yùn)行,越來(lái)越多的對(duì)象被分配到 eden 區(qū)。
當(dāng) Eden 區(qū)放不下時(shí),就會(huì)發(fā)生 minor GC(也被稱為 young GC),第1步當(dāng)然是要先標(biāo)識(shí)出不可達(dá)垃圾對(duì)象(即:下圖中的黃色塊),然后將可達(dá)對(duì)象,移動(dòng)到 s0 區(qū)(即:4 個(gè)淡藍(lán)色的方塊挪到 s0 區(qū)),然后將黃色的垃圾塊清理掉,這一輪過(guò)后,eden 區(qū)就成空的了。
注:這里其實(shí)已經(jīng)綜合運(yùn)用了“【標(biāo)記-清理 eden】 + 【標(biāo)記-復(fù)制 eden->s0】”算法。
繼續(xù),隨著對(duì)象的不斷分配,eden 空可能又滿了,這時(shí)會(huì)重復(fù)剛才的 minor GC 過(guò)程,不過(guò)要注意的是,這時(shí)候 s0 是空的,所以 s0 與 s1 的角色其實(shí)會(huì)互換,即:存活的對(duì)象,會(huì)從 eden 和 s1 區(qū),向 s0 區(qū)移動(dòng)。然后再把 eden 和 s1 區(qū)中的垃圾清除,這一輪完成后,eden 與 s1 區(qū)變成空的,如下圖。
**對(duì)于那些比較“長(zhǎng)壽”的對(duì)象一直在 s0 與 s1 中挪來(lái)挪去,一來(lái)很占地方,而且也會(huì)造成一定開(kāi)銷,降低 gc 效率,于是有了“代齡(age)”及“晉升”。 ** 對(duì)象在新生代的 3 個(gè)區(qū)(edge,s0,s1)之間,每次從 1 個(gè)區(qū)移到另 1 區(qū),年齡+1,在 young 區(qū)達(dá)到一定的年齡閾值后,將晉升到老年代。下圖中是 8,即:挪動(dòng) 8 次后,如果還活著,下次 minor GC 時(shí),將移動(dòng)到 Tenured區(qū)。
下圖是晉升的主要過(guò)程:對(duì)象先分配在新生代,經(jīng)過(guò)多次 Young GC 后,如果對(duì)象還活著,晉升到老年代。
如果老年代,最終也放滿了,就會(huì)發(fā)生 major GC(即 Full GC),由于老年代的的對(duì)象通常會(huì)比較多,因?yàn)闃?biāo)記-清理-整理(壓縮)的耗時(shí)通常會(huì)比較長(zhǎng),會(huì)讓?xiě)?yīng)用出現(xiàn)卡頓的現(xiàn)象,這也是為什么很多應(yīng)用要優(yōu)化,盡量避免或減少 Full GC (STW)的原因。
如果分配的新對(duì)象比較大,YGC之后,Eden 區(qū)放不下,但是 old 區(qū)可以放下時(shí),會(huì)直接分配到 old 區(qū)(即沒(méi)有晉升這一過(guò)程,直接到老年代了)。
如果分配的新對(duì)象比較大,Eden 區(qū)放不下,YGC之后發(fā)現(xiàn)S(TO)區(qū)域放不下,但是 old 區(qū)可以放下時(shí),直接會(huì)晉升到old區(qū)。
如果分配的新對(duì)象比較大,Eden 區(qū)放不下,YGC之后發(fā)現(xiàn)S(TO)區(qū)域放得下,但是超過(guò)了 pre tenured threshold值 ,直接會(huì)晉升到old區(qū)。
不算最新出現(xiàn)的神器 ZGC,歷史上出現(xiàn)過(guò) 7 種經(jīng)典的垃圾回收器。
這些回收器都是基于分代的,把 G1 除外,按回收的分代劃分,橫線以上的 3 種:Serial ,ParNew, Parellel Scavenge都是回收新生代的,橫線以下的 3 種:CMS,Serial Old, Parallel Old都是回收老年代的。
單線程用標(biāo)記-復(fù)制算法,快刀斬亂麻,單線程的好處避免上下文切換,早期的機(jī)器,大多是單核,也比較實(shí)用。但執(zhí)行期間,會(huì)發(fā)生 STW(Stop The World)。
Serial 的多線程版本,同樣會(huì) STW,在多核機(jī)器上會(huì)更適用。
ParNew 的升級(jí)版本(注重吞吐量),主要區(qū)別在于提供了兩個(gè)參數(shù): -XX:MaxGCPauseMillis最大垃圾回收停頓時(shí)間(通過(guò)犧牲Eden區(qū)回收范圍來(lái)控制時(shí)間); -XX:GCTimeRatio垃圾回收時(shí)間與總時(shí)間占比(主要是通過(guò)預(yù)先計(jì)算回收時(shí)間的平均值進(jìn)行預(yù)測(cè)相關(guān)執(zhí)行時(shí)間),通過(guò)這2個(gè)參數(shù),可以適當(dāng)控制回收的節(jié)奏,更關(guān)注于吞吐率,即總時(shí)間與垃圾回收時(shí)間的比例。充分利用CPU的執(zhí)行時(shí)間,提高吞吐效率。但是回收時(shí)間不應(yīng)當(dāng)最快和最短
因?yàn)槔夏甏膶?duì)象通常比較多,占用的空間通常也會(huì)更大,如果采用復(fù)制算法,得留 50%的空間用于復(fù)制,相當(dāng)不劃算,而且因?yàn)閷?duì)象多,從 1 個(gè)區(qū),復(fù)制到另 1 個(gè)區(qū),耗時(shí)也會(huì)比較長(zhǎng),所以老年代的收集,通常會(huì)采用“標(biāo)記-整理”法。從名字就可以看出來(lái),這是單線程(串行)的, 依然會(huì)有 STW。
一句話:Serial Old 的多線程版本。
全稱:Concurrent Mark Sweep,從名字上看,就能猜出它是并發(fā)多線程的。這是 JDK 7 中廣泛使用的收集器,有必要多說(shuō)一下,借一張網(wǎng)友的圖說(shuō)話:
相對(duì) Serial Old 收集器或 Parallel Old 收集器而言,這個(gè)明顯要復(fù)雜多了,分為 4 個(gè)階段:
1)Inital Mark 初始標(biāo)記:主要是標(biāo)記 GC Root 開(kāi)始的下級(jí)(注:僅下一級(jí))對(duì)象,這個(gè)過(guò)程會(huì) STW,但是跟 GC Root 直接關(guān)聯(lián)的下級(jí)對(duì)象不會(huì)很多,因此這個(gè)過(guò)程其實(shí)很快。
2)Concurrent Mark 并發(fā)標(biāo)記:根據(jù)上一步的結(jié)果,繼續(xù)向下標(biāo)識(shí)所有關(guān)聯(lián)的對(duì)象,直到這條鏈上的最盡頭。這個(gè)過(guò)程是多線程的,雖然耗時(shí)理論上會(huì)比較長(zhǎng),但是其它工作線程并不會(huì)阻塞,沒(méi)有 STW。
3)Remark 再標(biāo)志:為啥還要再標(biāo)記一次?因?yàn)榈?2 步并沒(méi)有阻塞其它工作線程,其它線程在標(biāo)識(shí)過(guò)程中,很有可能會(huì)產(chǎn)生新的垃圾。
4)Concurrent Sweep:并行清理,這里使用多線程以“Mark Sweep-標(biāo)記清理”算法,把垃圾清掉,其它工作線程仍然能繼續(xù)支行,不會(huì)造成卡頓。
試想下,高鐵上的垃圾清理員,從車廂一頭開(kāi)始吆喝“有需要扔垃圾的乘客,請(qǐng)把垃圾扔一下”,一邊工作一邊向前走,等走到車廂另一頭時(shí),剛才走過(guò)的位置上,可能又有乘客產(chǎn)生了新的空瓶垃圾。所以,要完全把這個(gè)車廂清理干凈的話,她應(yīng)該喊一下:所有乘客不要再扔垃圾了(STW),然后把新產(chǎn)生的垃圾收走。
當(dāng)然,因?yàn)閯偛乓呀?jīng)把收過(guò)一遍垃圾,所以這次收集新產(chǎn)生的垃圾,用不了多長(zhǎng)時(shí)間(即:STW 時(shí)間不會(huì)很長(zhǎng))。
等等,剛才我們不是提到過(guò)“標(biāo)記清理”法,會(huì)留下很多內(nèi)存碎片嗎?確實(shí),但是也沒(méi)辦法,如果換成“Mark Compact 標(biāo)記-整理”法,把垃圾清理后,剩下的對(duì)象也順便排整理,會(huì)導(dǎo)致這些對(duì)象的內(nèi)存地址發(fā)生變化,別忘了,此時(shí)其它線程還在工作,如果引用的對(duì)象地址變了,就天下大亂了。
雖然仍不完美,但是從這 4 步的處理過(guò)程來(lái)看,以往收集器中最讓人詬病的長(zhǎng)時(shí)間 STW,通過(guò)上述設(shè)計(jì),被分解成二次短暫的 STW,所以從總體效果上看,應(yīng)用在 GC 期間卡頓的情況會(huì)大大改善,這也是 CMS 一度十分流行的重要原因。
由于CMS并發(fā)清理階段用戶線程還在運(yùn)行著,伴隨程序運(yùn)行自然會(huì)有新垃圾產(chǎn)生,這部分垃圾得標(biāo)記過(guò)程之后,所以CMS無(wú)法在當(dāng)收集中處理掉他們,只好留待下一次GC清理掉,這一部分垃圾稱為浮動(dòng)垃圾。
在jdk1.5默認(rèn)設(shè)置下,CMS收集器當(dāng)老年代使用了68%的空間就會(huì)被激活,可以通過(guò)-XX:CMSInitialOccupancyFraction的值來(lái)提高觸發(fā)百分比,在jdk1.6中CMS啟動(dòng)閾值提升到了92%,要是CMS運(yùn)行期間預(yù)留的內(nèi)存無(wú)法滿足程序的需要,就會(huì)出現(xiàn)”Concurrent Mode Failure“,然后降級(jí)臨時(shí)啟用Serial Old收集器進(jìn)行老年代的垃圾收集,這樣停頓時(shí)間就很長(zhǎng)了。(當(dāng)存在并發(fā)清除的時(shí)候,業(yè)務(wù)線程還可以產(chǎn)生來(lái)及,結(jié)果造成CMS回收的時(shí)候,無(wú)法區(qū)存儲(chǔ)過(guò)大新生對(duì)象內(nèi)存快,導(dǎo)致出現(xiàn)CMF)
所以-XX:CMSInitialOccupancyFraction設(shè)置太高容易導(dǎo)致大量”Concurrent Mode Failure“。
CMS是一款基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,所以會(huì)產(chǎn)生空間碎片。為了解決這個(gè)問(wèn)題,CMS提供了-XX:UseCMSCompactAtFullCollection開(kāi)發(fā)參數(shù)用于開(kāi)啟內(nèi)存碎片的合并整理,由于內(nèi)存整理是無(wú)法并行的,所以停頓時(shí)間會(huì)變長(zhǎng)。還有-XX:CMSFullGCBeforeCompaction,這個(gè)參數(shù)用于設(shè)置多少次不壓縮Full GC后,跟著來(lái)一次帶壓縮的(默認(rèn)為0)。
CMS默認(rèn)啟動(dòng)的回收線程數(shù)是(cpu數(shù)量+3)/4。所以CPU數(shù)量少會(huì)導(dǎo)致用戶程序執(zhí)行速度降低較多。這就是它會(huì)降低吞吐量的原因之一。
G1 的全稱是 Garbage-First。鑒于 CMS 的一些不足之外,比如: 老年代內(nèi)存碎片化,STW 時(shí)間雖然已經(jīng)改善了很多,但是仍然有提升空間。G1 就橫空出世了,它對(duì)于 heap 區(qū)的內(nèi)存劃思路很新穎,有點(diǎn)算法中分治法“分而治之”的味道。
如下圖,G1 將 heap 內(nèi)存區(qū),劃分為一個(gè)個(gè)大小相等(1-32M,2 的 n 次方)、內(nèi)存連續(xù)的 Region 區(qū)域,每個(gè) region 都對(duì)應(yīng) Eden、Survivor 、Old、Humongous四種角色之一,但是 region 與 region 之間不要求連續(xù)。
注:Humongous,簡(jiǎn)稱 H 區(qū)是專用于存放超大對(duì)象的區(qū)域,通常>= 1/2 Region Size,且只有 Full GC 階段,才會(huì)回收 H 區(qū),避免了頻繁掃描、復(fù)制/移動(dòng)大對(duì)象。
所有的垃圾回收,都是基于 1 個(gè)個(gè) region 的。JVM 內(nèi)部知道,哪些 region 的對(duì)象最少(即:該區(qū)域最空),總是會(huì)優(yōu)先收集這些 region(因?yàn)閷?duì)象少,內(nèi)存相對(duì)較空,肯定快),這也是 Garbage-First 得名的由來(lái),G 即是 Garbage 的縮寫(xiě), 1 即 First。
理論上講,只要有一個(gè) Empty Region(空區(qū)域),就可以進(jìn)行垃圾回收。
由于region 與 region 之間并不要求連續(xù),而使用 G1 的場(chǎng)景通常是大內(nèi)存,比如 64G 甚至更大,為了提高掃描根對(duì)象和標(biāo)記的效率,G1 使用了二個(gè)新的輔助存儲(chǔ)結(jié)構(gòu):
Remembered Sets:簡(jiǎn)稱 RSets,用于根據(jù)每個(gè) region 里的對(duì)象,是從哪指向過(guò)來(lái)的(即:誰(shuí)引用了我),每個(gè) Region 都有獨(dú)立的 RSets。(Other Region -> Self Region)。
Collection Sets :簡(jiǎn)稱 CSets,記錄了等待回收的 Region 集合,GC 時(shí)這些 Region 中的對(duì)象會(huì)被回收(copied or moved)。
RSets的引入,在YGC時(shí),將新生代 Region 的 RSets 做為根對(duì)象,可以避免掃描老年代的 region,能大大減輕 GC 的負(fù)擔(dān)。注:在老年代收集 Mixed GC 時(shí),RSets 記錄了 Old->Old 的引用,也可以避免掃描所有 Old 區(qū)。
也稱為 Old Generation Collection
按 oracle 官網(wǎng)文檔描述分為 5 個(gè)階段:Initial Mark(STW) -> Root Region Scan -> Cocurrent Marking -> Remark(STW) -> Copying/Cleanup(STW && Concurrent)
注:也有很多文章會(huì)把 Root Region Scan 省略掉,合并到 Initial Mark 里,變成 4 個(gè)階段。
存活對(duì)象的“初始標(biāo)記”依賴于 Young GC,GC 日志中會(huì)記錄成 young 字樣。
2019-06-09T15:24:37.086+0800: 500993.392: [GC pause (G1 Evacuation Pause) (young), 0.0493588 secs] [Parallel Time: 41.9 ms, GC Workers: 8] [GC Worker Start (ms): Min: 500993393.7, Avg: 500993393.7, Max: 500993393.7, Diff: 0.1] [Ext Root Scanning (ms): Min: 1.5, Avg: 2.2, Max: 4.4, Diff: 2.8, Sum: 17.2] [Update RS (ms): Min: 15.8, Avg: 18.1, Max: 18.9, Diff: 3.1, Sum: 144.8] [Processed Buffers: Min: 110, Avg: 144.9, Max: 163, Diff: 53, Sum: 1159] [Scan RS (ms): Min: 4.7, Avg: 5.0, Max: 5.1, Diff: 0.4, Sum: 39.7] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Object Copy (ms): Min: 16.4, Avg: 16.5, Max: 16.6, Diff: 0.2, Sum: 132.0] [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Termination Attempts: Min: 1, Avg: 4.9, Max: 7, Diff: 6, Sum: 39] [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.3] [GC Worker Total (ms): Min: 41.7, Avg: 41.8, Max: 41.8, Diff: 0.1, Sum: 334.1] [GC Worker End (ms): Min: 500993435.5, Avg: 500993435.5, Max: 500993435.5, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.2 ms] [Other: 7.2 ms] [Choose CSet: 0.0 ms] [Ref Proc: 4.3 ms] [Ref Enq: 0.1 ms] [Redirty Cards: 0.1 ms] [Humongous Register: 0.1 ms] [Humongous Reclaim: 0.1 ms] [Free CSet: 0.6 ms] [Eden: 1340.0M(1340.0M)->0.0B(548.0M) Survivors: 40.0M->64.0M Heap: 2868.2M(12.0G)->1499.8M(12.0G)] [Times: user=0.35 sys=0.00, real=0.05 secs]
并發(fā)標(biāo)記過(guò)程中,如果發(fā)現(xiàn)某些 region 全是空的,會(huì)被直接清除。
進(jìn)入重新標(biāo)記階段。
并發(fā)復(fù)制/清查階段。這個(gè)階段,Young 區(qū)和 Old 區(qū)的對(duì)象有可能會(huì)被同時(shí)清理。GC 日志中,會(huì)記錄為 mixed 字段,這也是 G1 的老年代收集,也稱為 Mixed GC 的原因。
2019-06-09T15:24:23.959+0800: 500980.265: [GC pause (G1 Evacuation Pause) (mixed), 0.0885388 secs] [Parallel Time: 74.2 ms, GC Workers: 8] [GC Worker Start (ms): Min: 500980270.6, Avg: 500980270.6, Max: 500980270.6, Diff: 0.1] [Ext Root Scanning (ms): Min: 1.7, Avg: 2.2, Max: 4.1, Diff: 2.4, Sum: 17.3] [Update RS (ms): Min: 11.7, Avg: 13.7, Max: 14.3, Diff: 2.6, Sum: 109.8] [Processed Buffers: Min: 136, Avg: 141.5, Max: 152, Diff: 16, Sum: 1132] [Scan RS (ms): Min: 42.5, Avg: 42.9, Max: 43.1, Diff: 0.5, Sum: 343.1] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] [Object Copy (ms): Min: 14.9, Avg: 15.2, Max: 15.4, Diff: 0.5, Sum: 121.7] [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] [Termination Attempts: Min: 1, Avg: 8.2, Max: 11, Diff: 10, Sum: 66] [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2] [GC Worker Total (ms): Min: 74.0, Avg: 74.0, Max: 74.1, Diff: 0.1, Sum: 592.3] [GC Worker End (ms): Min: 500980344.6, Avg: 500980344.6, Max: 500980344.6, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.5 ms] [Other: 13.9 ms] [Choose CSet: 4.1 ms] [Ref Proc: 1.8 ms] [Ref Enq: 0.1 ms] [Redirty Cards: 0.2 ms] [Humongous Register: 0.1 ms] [Humongous Reclaim: 0.1 ms] [Free CSet: 5.6 ms] [Eden: 584.0M(584.0M)->0.0B(576.0M) Survivors: 28.0M->36.0M Heap: 4749.3M(12.0G)->2930.0M(12.0G)] [Times: user=0.61 sys=0.00, real=0.09 secs]
通過(guò)這幾個(gè)階段的分析,雖然看上去很多階段仍然會(huì)發(fā)生 STW,但是 G1 提供了一個(gè)預(yù)測(cè)模型,通過(guò)統(tǒng)計(jì)方法,根據(jù)歷史數(shù)據(jù)來(lái)預(yù)測(cè)本次收集,需要選擇多少個(gè) Region 來(lái)回收,盡量滿足用戶的預(yù)期停頓值(-XX:MaxGCPauseMillis參數(shù)可指定預(yù)期停頓值)。
注:如果 Mixed GC 仍然效果不理想,跟不上新對(duì)象分配內(nèi)存的需求,會(huì)使用 Serial Old GC(Full GC)強(qiáng)制收集整個(gè) Heap。
小結(jié):與 CMS 相比,G1 有內(nèi)存整理過(guò)程(標(biāo)記-壓縮),避免了內(nèi)存碎片;STW 時(shí)間可控(能預(yù)測(cè) GC 停頓時(shí)間)。
上述內(nèi)容就是Java中JVM內(nèi)存布局的GC原理是怎樣的,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)站名稱:Java中JVM內(nèi)存布局的GC原理是怎樣的
路徑分享:http://jinyejixie.com/article16/pggodg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、外貿(mào)建站、云服務(wù)器、網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)