G1垃圾回收器


垃圾回收器的發展歷程

file

背景

01、G1解決的問題

G1垃圾回收器是04年正式提出,12開始正式支持,在17年作為JDK9默認的垃圾處理器。
file

04年的時候,java程序堆的內存越來越大,從而導致程序中可存活的活對象越來越多,因此GCSTW時間越來越長。這是G1要解決的主要問題:STW帶來的停頓時間太長了

CMS在此之前效率也很高,但活對象數量一多,STW時間也很長。而且CMS無法解決內存碎片化的問題。

G1還解決的問題是:CMSGC后,無法compact內存。

02、G1達成的目標

(1)減少由於STW而帶來的程序延遲時間,做到偽實時、低延時、可設定目標;
可設定目標是指能夠設置GC最大STW停頓的時間,G1會盡量達成目的,但不一定達成。

-XX:MaxGCPauseMillis=N

默認情況下是250毫秒

(2)解決CMSGC后,無法壓縮程序內存的問題;

(3)在JDK9之后,默認的垃圾處理器就是G1;它適用於堆內存較大的情況下(>4~6G);

G1垃圾回收器

一、G1內存布局

G1不再遵循之前的堆中對象的分代排列,而是將堆分成若干個等大的區域。
file

而是變成:
file

默認是分成2048個區域,-XX:G1HeapRegionSize=N 2048

Humongous:當你分配的一個對象超過一半區域的大小時,這個對象就會被放入這個區域。這個區域屬於老年代區域。

二、G1的介紹

G1垃圾回收器不再回收整個堆,而是選擇一個Collection SetCS)。而且每次GC時,會估計每個Region中的垃圾比例,優先回收垃圾多的Region。這就為什么被叫做Garbage First算法。這也是為什么G1可以控制STW停頓時間的原因。
G1含有三種GC算法:

  • Full young GC:年輕代GC算法:STWParallelCopying
  • 老年代GC算法:Mostly-concurrent markingIncremental compaction
  • Mixed GC:混合GC

三、G1引來的問題

問題描述

G1將年輕代、老年代區域划分為許多個小區域,增加在GC判斷對象是否為垃圾的難度。比如:

  • 老年代對象可能持有年代代的引用(跨代引用)
  • 不同的Region間的互相引用
    跨代/跨Region引用

假設在Full young GC時,某個年輕代Region對象可能被老年代的某個對象引用,那么我在回收這個年輕代Region時,怎么知道這里面的對象是否被其他Region、老年代引用呢?

問題解決

Remembered SetCard Table
file

1、CardTable
每個Region中分為很多區域,每個區域我們成為CardTable,對應的就是上述藍色區域;每個CardTable有多個entry組成。當對應的內存空間發生改變時,就會標記為dirty

2、RememberedSet
Region1CardTable引用Region2CardTable時,Region2RememberedSet就會記錄對應CardTable中的entry,可以根據其找到對應的內存區域。

3、解析
當某個內存對應進行賦值是,就是對象的set方法,我們可以在這種方法上添加dirty的描述。
這其實就是典型的時間換空間的做法:用額外的空間維護引用信息,這就是占用5~10%的過多內存占用。

解決方法的實現

1、Write Barrier介紹
Write barrier是一種向JVM注入的一小段代碼,用於記錄指針變化。比如說object.field = <reference>

JVM開始更新指針時,就經過以下幾步:

  • 標記CardDirty
  • Card存入Dirty Card Queue隊列中

這里有一個問題:為什么要放在隊列里,而不是直接去更新RememberedSet呢?
這是因為JVM運行可能會有多個線程並行的修改RememberedSet,這樣就需要花費額外的時間來解決多線程同步問題。而這種更新引用是頻繁的,所以這種額外時間是無法忍受的。

2、Dirty Card Queue
這個隊列有白、綠、黃、紅四個顏色,表示應用線程往這個隊列放任務的狀態。

  • White
    表示沒有應用線程往隊列里放任務,什么事都不用干。

  • Green
    此時Refinement線程開始被激活,開始更新RS-XX:G1ConcRefinementGreenZone=N

  • Yellow
    此時全部的Refinement線程都被激活,來更新RS-XX:G1ConcRefinementYellowZone=N

  • Red
    這個時候,應用線程也開始參與排空隊列的工作。-XX:G1ConcRefinementRedZone=N

四、GC算法的過程

1、Fully young GC

GC的過程

(1)STW
此時會暫停所有堆中的對象,將部分Region拷貝到指定區域。
file

(2)構建Collection Set
fully young GC就是選取所有的EdenSurvivor

(3)掃描GC Roots

(4)更新RememberedSet
排空Dirty Card Queue

(5)Process RS
根據RS找到要GC的對象被哪些對象引用了。

(6)對象拷貝
survivor區域對象的調整。

(7)Reference Processing

額外會做的事

G1記錄每個階段的時間,用於后期自動調優。比如說會記錄EdenSurvivor的數量和GC時間,后期會根據我們之前設定的暫停目標來自動調整Region數量。
但是我們設置暫停目標越短,年輕代的Region數量就越少。但這可能會導致Fully young GC頻繁發生。

2、Old GC

當堆用量達到一定程度時,就會觸發old GC。可以通過以下參數進行設置:

-XX:InitatingHeapOccpancyPercent=45

old GC有一個很大特點就是並發進行的。但它是如何在堆中不斷變化的情況下,確定哪些是要清理的垃圾對象呢?

三色標記算法

這種算法實現了在不暫停應用線程的情況下進行並發標記,標記過程過如下:
(1)將GC Root對象記錄為黑色,其直接引用對象記錄為灰色,並將這些灰色對象放入一個隊列中
file
(2)從隊列取出對象,將其標為黑色,將其引用對象記錄為灰色,再放入隊列中
file
(3)直到隊列中無對象為止
file

三色標記算法的缺點:Lost Object Problem

三色標記算法並沒有完全將所有的活對象都標記出來,這就是Lost Object Problem問題。比如說:
(1)剛開始時

file

(2)在即將描述將C標為灰色的一剎那

file

此時,C依然是活對象,但是已經無法將其標記了。

(3)結果

file

Lost Object Problem的解決

這種解決辦法還是通過Write barrier技術來解決。當B.c=null,也就是C指針被刪除時,G1還是被認為活對象。

那如果C是新生對象呢?這是老年代GC

Old GC過程

(1)STW
老年代GC會在這個時候,進行一次Fully young GC

(2)恢復應用線程

(3)使用三色標記算法並發標記(init marking

(4)STW

這時候會有一個Remark階段,主要是解決SATBReference processing
還會有一個Cleanup階段,用於回收全為空的區

(5)恢復應用線程

3、Mixed GC

我們直到CMS最大的缺點就是無法進行壓縮操作,而G1就通過Mixed GC解決了這個問題。

Mixed GC沒有固定觸發條件,他是根據Fully young GC收集的信息和我們配置的時間來決定,是否觸發Mixed GC。它會根據暫停目標,來優先選擇垃圾最多的Old Region來執行。

Mixed GC會選擇若干個Region進行,默認是選擇1/8Old RegionEden RegionSurvivor Region

Mixed GC的過程跟Fully young GC的過程相同,都是:STWParallelCopying

原博客地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM