G1垃圾回收器
垃圾回收器的發展歷程
背景
01、G1
解決的問題
G1
垃圾回收器是04
年正式提出,12
開始正式支援,在17
年作為JDK9
預設的垃圾處理器。
在04
年的時候,java
程式堆的記憶體越來越大,從而導致程式中可存活的活物件越來越多,因此GC
的STW
時間越來越長。這是G1
要解決的主要問題:STW
帶來的停頓時間太長了。
CMS
在此之前效率也很高,但活物件數量一多,STW
時間也很長。而且CMS
無法解決記憶體碎片化的問題。
G1
還解決的問題是:CMS
在GC
後,無法compact
記憶體。
02、G1
達成的目標
(1)減少由於STW
而帶來的程式延遲時間,做到偽實時、低延時、可設定目標;
可設定目標是指能夠設定GC
STW
停頓的時間,G1
會盡量達成目的,但不一定達成。
-XX:MaxGCPauseMillis=N
預設情況下是250毫秒
(2)解決CMS
在GC
後,無法壓縮程式記憶體的問題;
(3)在JDK9
之後,預設的垃圾處理器就是G1
;它適用於堆記憶體較大的情況下(>4~6G
);
G1
垃圾回收器
一、G1
記憶體佈局
G1
不再遵循之前的堆中物件的分代排列,而是將堆分成若干個等大的區域。
而是變成:
預設是分成
2048
個區域,-XX:G1HeapRegionSize=N 2048
Humongous
:當你分配的一個物件超過一半區域的大小時,這個物件就會被放入這個區域。這個區域屬於老年代區域。
二、G1
的介紹
G1
垃圾回收器不再回收整個堆,而是選擇一個Collection Set
(CS
)。而且每次GC
時,會估計每個Region
中的垃圾比例,優先回收垃圾多的Region
。這就為什麼被叫做Garbage First
演算法。這也是為什麼G1
可以控制STW
停頓時間的原因。
G1
含有三種GC
演算法:
Full young GC
:年輕代GC
演算法:STW
、Parallel
、Copying
- 老年代
GC
演算法:Mostly-concurrent marking
、Incremental compaction
Mixed GC
:混合GC
三、G1
引來的問題
問題描述
G1
將年輕代、老年代區域劃分為許多個小區域,增加在GC
- 老年代物件可能持有年代代的引用(跨代引用)
- 不同的
Region
間的互相引用
假設在Full young GC
時,某個年輕代Region
物件可能被老年代的某個物件引用,那麼我在回收這個年輕代Region
時,怎麼知道這裡面的物件是否被其他Region
、老年代引用呢?
問題解決
Remembered Set
、Card Table
1、CardTable
每個Region
中分為很多區域,每個區域我們成為CardTable
,對應的就是上述藍色區域;每個CardTable
有多個entry
組成。當對應的記憶體空間發生改變時,就會標記為dirty
。
2、RememberedSet
當Region1
的CardTable
引用Region2
的CardTable
時,Region2
的RememberedSet
就會記錄對應CardTable
中的entry
,可以根據其找到對應的記憶體區域。
3、解析
當某個記憶體對應進行賦值是,就是物件的set
方法,我們可以在這種方法上新增dirty
的描述。
這其實就是典型的時間換空間的做法:用額外的空間維護引用資訊,這就是佔用5~10%
的過多記憶體佔用。
解決方法的實現
1、Write Barrier
介紹
Write barrier
是一種向JVM
注入的一小段程式碼,用於記錄指標變化。比如說object.field = <reference>
。
在JVM
開始更新指標時,就經過以下幾步:
- 標記
Card
為Dirty
- 將
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
拷貝到指定區域。
(2)構建Collection Set
fully young GC
就是選取所有的Eden
和Survivor
。
(3)掃描GC Roots
(4)更新RememberedSet
排空Dirty Card Queue
(5)Process RS
根據RS
找到要GC
的物件被哪些物件引用了。
(6)物件拷貝
survivor
區域物件的調整。
(7)Reference Processing
額外會做的事
G1
記錄每個階段的時間,用於後期自動調優。比如說會記錄Eden
、Survivor
的數量和GC
時間,後期會根據我們之前設定的暫停目標來自動調整Region
數量。
但是我們設定暫停目標越短,年輕代的Region
數量就越少。但這可能會導致Fully young GC
頻繁發生。
2、Old GC
當堆用量達到一定程度時,就會觸發old GC
。可以通過以下引數進行設定:
-XX:InitatingHeapOccpancyPercent=45
old GC
有一個很大特點就是併發進行的。但它是如何在堆中不斷變化的情況下,確定哪些是要清理的垃圾物件呢?
三色標記演算法
這種演算法實現了在不暫停應用執行緒的情況下進行併發標記,標記過程過如下:
(1)將GC Root
物件記錄為黑色,其直接引用物件記錄為灰色,並將這些灰色物件放入一個佇列中
(2)從佇列取出物件,將其標為黑色,將其引用物件記錄為灰色,再放入佇列中
(3)直到佇列中無物件為止
三色標記演算法的缺點:Lost Object Problem
三色標記演算法並沒有完全將所有的活物件都標記出來,這就是Lost Object Problem
問題。比如說:
(1)剛開始時
(2)在即將描述將C
標為灰色的一剎那
此時,C
依然是活物件,但是已經無法將其標記了。
(3)結果
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
階段,主要是解決SATB
、Reference processing
還會有一個Cleanup
階段,用於回收全為空的區
(5)恢復應用執行緒
3、Mixed GC
我們直到CMS
最大的缺點就是無法進行壓縮操作,而G1
就通過Mixed GC
解決了這個問題。
Mixed GC
沒有固定觸發條件,他是根據Fully young GC
收集的資訊和我們配置的時間來決定,是否觸發Mixed GC
。它會根據暫停目標,來優先選擇垃圾最多的Old Region
來執行。
Mixed GC
會選擇若干個Region
進行,預設是選擇1/8
的Old Region
、Eden Region
、Survivor Region
。
Mixed GC
的過程跟Fully young GC
的過程相同,都是:STW
、Parallel
、Copying
。
原部落格地址