摘要:之前根據的內存管理白皮書介紹了在分代算法中的幾個垃圾收集器,本文將介紹垃圾收集器。本節介紹的收集過程,收集器主要包括了以下種操作年輕代收集并發收集,和應用線程同時執行混合式垃圾收集必要時的接下來,我們進行一一介紹。
之前根據 Sun 的內存管理白皮書介紹了在 HotSpot JVM 分代算法中的幾個垃圾收集器,本文將介紹 G1 垃圾收集器。
G1 的主要關注點在于達到可控的停頓時間,在這個基礎上盡可能提高吞吐量,這一點非常重要。
G1 被設計用來長期取代 CMS 收集器,和 CMS 相同的地方在于,它們都屬于并發收集器,在大部分的收集階段都不需要掛起應用程序。區別在于,G1 沒有 CMS 的碎片化問題(或者說不那么嚴重),同時提供了更加可控的停頓時間。
如果你的應用使用了較大的堆(如 6GB 及以上)而且還要求有較低的垃圾收集停頓時間(如 0.5 秒),那么 G1 是你絕佳的選擇,是時候放棄 CMS 了。
閱讀建議:本文力求用簡單的話介紹清楚 G1 收集器,但是并不會重復介紹每一個細節,所以希望讀者了解其他幾個收集器的工作過程,尤其是 CMS 收集器。
G1 總覽首先是內存劃分上,之前介紹的分代收集器將整個堆分為年輕代、老年代和永久代,每個代的空間是確定的。
而 G1 將整個堆劃分為一個個大小相等的小塊(每一塊稱為一個 region),每一塊的內存是連續的。和分代算法一樣,G1 中每個塊也會充當 Eden、Survivor、Old 三種角色,但是它們不是固定的,這使得內存使用更加地靈活。
執行垃圾收集時,和 CMS 一樣,G1 收集線程在標記階段和應用程序線程并發執行,標記結束后,G1 也就知道哪些區塊基本上是垃圾,存活對象極少,G1 會先從這些區塊下手,因為從這些區塊能很快釋放得到很大的可用空間,這也是為什么 G1 被取名為 Garbage-First 的原因。
在 G1 中,目標停頓時間非常非常重要,用 -XX:MaxGCPauseMillis=200 指定期望的停頓時間。
G1 使用了停頓預測模型來滿足用戶指定的停頓時間目標,并基于目標來選擇進行垃圾回收的區塊數量。G1 采用增量回收的方式,每次回收一些區塊,而不是整堆回收。
我們要知道 G1 不是一個實時收集器,它會盡力滿足我們的停頓時間要求,但也不是絕對的,它基于之前垃圾收集的數據統計,估計出在用戶指定的停頓時間內能收集多少個區塊。
注意:G1 有和應用程序一起運行的并發階段,也有 stop-the-world 的并行階段。但是,Full GC 的時候還是單線程運行的,所以我們應該盡量避免發生 Full GC,后面我們也會介紹什么時候會觸發 Full GC。
G1 內存占用
注:這里不那么重要。
G1 比 ParallelOld 和 CMS 會需要更多的內存消耗,那是因為有部分內存消耗于簿記(accounting)上,如以下兩個數據結構:
Remembered Sets:每個區塊都有一個 RSet,用于記錄進入該區塊的對象引用(如區塊 A 中的對象引用了區塊 B,區塊 B 的 Rset 需要記錄這個信息),它用于實現收集過程的并行化以及使得區塊能進行獨立收集??傮w上 Remembered Sets 消耗的內存小于 5%。
Collection Sets:將要被回收的區塊集合。GC 時,在這些區塊中的對象會被復制到其他區塊中,總體上 Collection Sets 消耗的內存小于 1%。
前面啰里啰嗦說了挺多的,唯一要記住的就是,G1 的設計目標就是盡力滿足我們的目標停頓時間上的要求。
本節介紹 G1 的收集過程,G1 收集器主要包括了以下 4 種操作:
1、年輕代收集
2、并發收集,和應用線程同時執行
3、混合式垃圾收集
*、必要時的 Full GC
接下來,我們進行一一介紹。
首先,我們來看下 G1 的堆結構:
年輕代中的垃圾收集流程(Young GC):
我們可以看到,年輕代收集概念上和之前介紹的其他分代收集器大差不差的,但是它的年輕代會動態調整。
接下來是 Old GC 的流程(含 Young GC 階段),其實把 Old GC 理解為并發周期是比較合理的,不要單純地認為是清理老年代的區塊,因為這一步和年輕代收集也是相關的。下面我們介紹主要流程:
1.初始標記:stop-the-world,它伴隨著一次普通的 Young GC 發生,然后對 Survivor 區(root region)進行標記,因為該區可能存在對老年代的引用。
因為 Young GC 是需要 stop-the-world 的,所以并發周期直接重用這個階段,雖然會增加 CPU 開銷,但是停頓時間只是增加了一小部分。
2.掃描根引用區:掃描 Survivor 到老年代的引用,該階段必須在下一次 Young GC 發生前結束。
這個階段不能發生年輕代收集,如果中途 Eden 區真的滿了,也要等待這個階段結束才能進行 Young GC。
3.并發標記:尋找整個堆的存活對象,該階段可以被 Young GC 中斷。
這個階段是并發執行的,中間可以發生多次 Young GC,Young GC 會中斷標記過程
4.重新標記:stop-the-world,完成最后的存活對象標記。使用了比 CMS 收集器更加高效的 snapshot-at-the-beginning (SATB) 算法。
Oracle 的資料顯示,這個階段會回收完全空閑的區塊
5.清理:清理階段真正回收的內存很少。
到這里,G1 的一個并發周期就算結束了,其實就是主要完成了垃圾定位的工作,定位出了哪些分區是垃圾最多的。
并發周期結束后是混合垃圾回收周期,不僅進行年輕代垃圾收集,而且回收之前標記出來的老年代的垃圾最多的部分區塊。
混合垃圾回收周期會持續進行,直到幾乎所有的被標記出來的分區(垃圾占比大的分區)都得到回收,然后恢復到常規的年輕代垃圾收集,最終再次啟動并發周期。
Full GC到這里我們已經說了年輕代收集、并發周期、混合回收周期了,大家要熟悉這幾個階段的工作。
下面我們來介紹特殊情況,那就是會導致 Full GC 的情況,也是我們需要極力避免的:
1.concurrent mode failure:并發模式失敗,CMS 收集器也有同樣的概念。G1 并發標記期間,如果在標記結束前,老年代被填滿,G1 會放棄標記。
這個時候說明堆需要增加了,或者需要調整并發周期,如增加并發標記的線程數量,讓并發標記盡快結束或者就是更早地進行并發周期,默認是整堆內存的 45% 被占用就開始進行并發周期。
2.晉升失?。翰l周期結束后,是混合垃圾回收周期,伴隨著年輕代垃圾收集,進行清理老年代空間,如果這個時候清理的速度小于消耗的速度,導致老年代不夠用,那么會發生晉升失敗。
說明混合垃圾回收需要更迅速完成垃圾收集,也就是說在混合回收階段,每次年輕代的收集應該處理更多的老年代已標記區塊。
3.疏散失?。耗贻p代垃圾收集的時候,如果 Survivor 和 Old 區沒有足夠的空間容納所有的存活對象。這種情況肯定是非常致命的,因為基本上已經沒有多少空間可以用了,這個時候會觸發 Full GC 也是很合理的。
最簡單的就是增加堆大小
4.大對象分配失敗,我們應該盡可能地不創建大對象,尤其是大于一個區塊大小的那種對象。
簡單小結看完上面的 Young GC 和 Old GC 等,很多讀者可能還是很懵的,這里說幾句不嚴謹的白話文幫助讀者進行理解:
首先,最好不要把上面的 Old GC 當做是一次 GC 來看,而應該當做并發標記周期來理解,雖然它確實會釋放出一些內存。
并發標記結束后,G1 也就知道了哪些區塊是最適合被回收的,那些完全空閑的區塊會在這這個階段被回收。如果這個階段釋放了足夠的內存出來,其實也就可以認為結束了一次 GC。
我們假設并發標記結束了,那么下次 GC 的時候,還是會先回收年輕代,如果從年輕代中得到了足夠的內存,那么結束;過了幾次后,年輕代垃圾收集不能滿足需要了,那么就需要利用之前并發標記的結果,選擇一些活躍度最低的老年代區塊進行回收。直到最后,老年代會進入下一個并發周期。
那么什么時候會啟動并發標記周期呢?這個是通過參數控制的,下面馬上要介紹這個參數了,此參數默認值是 45,也就是說當堆空間使用了 45% 后,G1 就會進入并發標記周期。
G1 參數配置和最佳實踐G1 調優的目標是盡量避免出現 Full GC,其實就是給老年代足夠的空間,或相對更多的空間。
有以下幾點我們可以進行調整的方向:
增加堆大小,或調整老年代和年輕代的比例,這個很好理解
增加并發周期的線程數量,其實就是為了加快并發周期快點結束
讓并發周期盡早開始,這個是通過設置堆使用占比來調整的(默認 45%)
在混合垃圾回收周期中回收更多的老年代區塊
G1 的很重要的目標是達到可控的停頓時間,所以很多的行為都以這個目標為出發點開展的。
我們通過設置 -XX:MaxGCPauseMillis=N 來指定停頓時間(單位 ms,默認 200ms),如果沒有達到這個目標,G1 會通過各種方式來補救:調整年輕代和老年代的比例,調整堆大小,調整晉升的年齡閾值,調整混合垃圾回收周期中處理的老年代的區塊數量等等。
當然了,調整每個參數滿足了一個條件的同時往往也會引入另一個問題,比如為了降低停頓時間,我們可以減小年輕代的大小,可是這樣的話就會增加年輕代垃圾收集的頻率。如果我們減少混合垃圾回收周期處理的老年代區塊數量,雖然可以更容易滿足停頓時間要求,可是這樣就會增加 Full GC 的風險等等。
下面介紹最常用也是最基礎的一些參數的設置,涉及到更高級的調優參數設置,請讀者自行參閱其他資料。
參數介紹:
-XX:+UseG1GC 使用 G1 收集器
-XX:MaxGCPauseMillis=200 指定目標停頓時間,默認值 200 毫秒。
在設置 -XX:MaxGCPauseMillis 值的時候,不要指定為平均時間,而應該指定為滿足 90% 的停頓在這個時間之內。記住,停頓時間目標是我們的目標,不是每次都一定能滿足的。
-XX:InitiatingHeapOccupancyPercent=45 整堆使用達到這個比例后,觸發并發 GC 周期,默認 45%。
如果要降低晉升失敗的話,通??梢哉{整這個數值,使得并發周期提前進行
-XX:NewRatio=n
老年代/年輕代,默認值 2,即 1/3 的年輕代,2/3 的老年代老年代/年輕代,默認值 2,即 1/3 的年輕代,2/3 的老年代。不要設置年輕代為固定大小,否則:G1 不再需要滿足我們的停頓時間目標,不能再按需擴容或縮容年輕代大小
-XX:SurvivorRatio=n
Eden/Survivor,默認值 8,這個和其他分代收集器是一樣的
-XX:MaxTenuringThreshold =n
從年輕代晉升到老年代的年齡閾值,也是和其他分代收集器一樣的
-XX:ParallelGCThreads=n
并行收集時候的垃圾收集線程數
-XX:ConcGCThreads=n
并發標記階段的垃圾收集線程數,增加這個值可以讓并發標記更快完成,如果沒有指定這個值,JVM 會通過以下公式計算得到ConcGCThreads=(ParallelGCThreads + 2) / 4^3
-XX:G1ReservePercent=n
堆內存的預留空間百分比,默認 10,用于降低晉升失敗的風險,即默認地會將 10% 的堆內存預留下來。
-XX:G1HeapRegionSize=n
每一個 region 的大小,默認值為根據堆大小計算出來,取值 1MB~32MB,這個我們通常指定整堆大小就好了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72449.html
摘要:表示允許垃圾收集線程處理本次垃圾收集開始前沒有處理好的日志緩沖區,這可以確保當前分區的是最新的。垃圾收集線程在完成其他任務的時間展示每個垃圾收集線程的最小最大平均差值和總共時間。 本文翻譯自:https://www.redhat.com/en/blog/collecting-and-reading-g1-garbage-collector-logs-part-2?source=auth...
摘要:虛擬機所處的區域,則表示它是屬于新生代收集器還是老年代收集器。虛擬機總共運行了分鐘,其中垃圾收集花掉分鐘,那么吞吐量就是。收集器線程所占用的數量為。 本文主要從GC(垃圾回收)的角度試著對jvm中的內存分配策略與相應的垃圾收集器做一個介紹。 注:還是老規矩,本著能畫圖就不BB原則,盡量將各知識點通過思維導圖或者其他模型圖的方式進行說明。文字僅記錄額外的思考與心得,以及其他特殊情況 內存...
摘要:深入理解虛擬機高級特性與最佳實踐第二版讀書筆記與常見面試題總結上篇文章傳送門深入理解虛擬機之內存區域本節常見面試題推薦帶著問題閱讀,問題答案在文中都有提到如何判斷對象是否死亡兩種方法。虛引用主要用來跟蹤對象被垃圾回收的活動。 《深入理解Java虛擬機:JVM高級特性與最佳實踐(第二版》讀書筆記與常見面試題總結 上篇文章傳送門: 深入理解虛擬機之Java內存區域 本節常見面試題(推薦帶著...
摘要:之前的堆內存示意圖從上圖可以看出堆內存的分為新生代老年代和永久代。對象優先在區分配目前主流的垃圾收集器都會采用分代回收算法,因此需要將堆內存分為新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。 上文回顧:《可能是把Java內存區域講的最清楚的一篇文章》 寫在前面 本節常見面試題: 問題答案在文中都有提到 如何判斷對象是否死亡(兩種方法)。 簡單的介紹一下強引用...
摘要:此外,從結果我們可以得知,一個堆對象的放在局部變量表中的第一項引用會永遠存在,在方法體內可以將引用賦值給其他變量,這樣堆中對象就可以被其他變量所引用,即不會被回收。 原創不易,如需轉載,請注明出處https://www.cnblogs.com/baixianlong/p/10697554.html,多多支持哈! 一、什么是GC? GC是垃圾收集的意思,內存處理是編程人員容易出現問題的地...
閱讀 3012·2021-11-24 10:22
閱讀 3059·2021-11-23 10:10
閱讀 1369·2021-09-28 09:35
閱讀 1761·2019-08-29 13:16
閱讀 1403·2019-08-26 13:29
閱讀 2798·2019-08-26 10:27
閱讀 689·2019-08-26 10:09
閱讀 1452·2019-08-23 18:05