1:瞭解G1
G1的第一篇paper(附錄1)發表於2004年,在2012年纔在jdk1.7u4中可用。oracle官方計劃在jdk9中將G1變成默認的垃圾收集器,以替代CMS。爲什麼oracle要極力推薦G1呢,G1有哪些優勢?java
首先,G1的設計原則就是簡單可行的性能調優算法
開發人員僅僅須要聲明如下參數便可:數組
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200併發
其中-XX:+UseG1GC爲開啓G1垃圾收集器,-Xmx32g 設計堆內存的最大內存爲32G,-XX:MaxGCPauseMillis=200設置GC的最大暫停時間爲200ms。若是咱們須要調優,在內存大小必定的狀況下,咱們只須要修改最大暫停時間便可。oracle
其次,G1將新生代,老年代的物理空間劃分取消了。性能
這樣咱們不再用單獨的空間對每一個代進行設置了,不用擔憂每一個代內存是否足夠。線程
取而代之的是,G1算法將堆劃分爲若干個區域(Region),它仍然屬於分代收集器。不過,這些區域的一部分包含新生代,新生代的垃圾收集依然採用暫停全部應用線程的方式,將存活對象拷貝到老年代或者Survivor空間。老年代也分紅不少區域,G1收集器經過將對象從一個區域複製到另一個區域,完成了清理工做。這就意味着,在正常的處理過程當中,G1完成了堆的壓縮(至少是部分堆的壓縮),這樣也就不會有cms內存碎片問題的存在了。設計
在G1中,還有一種特殊的區域,叫Humongous區域。 若是一個對象佔用的空間超過了分區容量50%以上,G1收集器就認爲這是一個巨型對象。這些巨型對象,默認直接會被分配在年老代,可是若是它是一個短時間存在的巨型對象,就會對垃圾收集器形成負面影響。爲了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。若是一個H區裝不下一個巨型對象,那麼G1會尋找連續的H分區來存儲。爲了能找到連續的H區,有時候不得不啓動Full GC。指針
PS:在java 8中,持久代也移動到了普通的堆內存空間中,改成元空間。對象
對象分配策略
提及大對象的分配,咱們不得不談談對象的分配策略。它分爲3個階段:
TLAB(Thread Local Allocation Buffer)線程本地分配緩衝區
Eden區中分配
Humongous區分配
TLAB爲線程本地分配緩衝區,它的目的爲了使對象儘量快的分配出來。若是對象在一個共享的空間中分配,咱們須要採用一些同步機制來管理這些空間內的空閒空間指針。在Eden空間中,每個線程都有一個固定的分區用於分配對象,即一個TLAB。分配對象時,線程之間再也不須要進行任何的同步。
對TLAB空間中沒法分配的對象,JVM會嘗試在Eden空間中進行分配。若是Eden空間沒法容納該對象,就只能在老年代中進行分配空間。
最後,G1提供了兩種GC模式,Young GC和Mixed GC,兩種都是Stop The World(STW)的。下面咱們將分別介紹一下這2種模式。
2:G1 Young GC
Young GC主要是對Eden區進行GC,它在Eden空間耗盡時會被觸發。在這種狀況下,Eden空間的數據移動到Survivor空間中,若是Survivor空間不夠,Eden空間的部分數據會直接晉升到年老代空間。Survivor區的數據移動到新的Survivor區中,也有部分數據晉升到老年代空間中。最終Eden空間的數據爲空,GC中止工做,應用線程繼續執行。
這時,咱們須要考慮一個問題,若是僅僅GC 新生代對象,咱們如何找到全部的根對象呢? 老年代的全部對象都是根麼?那這樣掃描下來會耗費大量的時間。因而,G1引進了RSet的概念。它的全稱是Remembered Set,做用是跟蹤指向某個heap區內的對象引用。
在CMS中,也有RSet的概念,在老年代中有一塊區域用來記錄指向新生代的引用。這是一種point-out,在進行Young GC時,掃描根時,僅僅須要掃描這一塊區域,而不須要掃描整個老年代。
但在G1中,並無使用point-out,這是因爲一個分區過小,分區數量太多,若是是用point-out的話,會形成大量的掃描浪費,有些根本不須要GC的分區引用也掃描了。因而G1中使用point-in來解決。point-in的意思是哪些分區引用了當前分區中的對象。這樣,僅僅將這些對象當作根來掃描就避免了無效的掃描。因爲新生代有多個,那麼咱們須要在新生代之間記錄引用嗎?這是沒必要要的,緣由在於每次GC時,全部新生代都會被掃描,因此只須要記錄老年代到新生代之間的引用便可。
須要注意的是,若是引用的對象不少,賦值器須要對每一個引用作處理,賦值器開銷會很大,爲了解決賦值器開銷這個問題,在G1 中又引入了另一個概念,卡表(Card Table)。一個Card Table將一個分區在邏輯上劃分爲固定大小的連續區域,每一個區域稱之爲卡。卡一般較小,介於128到512字節之間。Card Table一般爲字節數組,由Card的索引(即數組下標)來標識每一個分區的空間地址。默認狀況下,每一個卡都未被引用。當一個地址空間被引用時,這個地址空間對應的數組索引的值被標記爲」0″,即標記爲髒被引用,此外RSet也將這個數組下標記錄下來。通常狀況下,這個RSet實際上是一個Hash Table,Key是別的Region的起始地址,Value是一個集合,裏面的元素是Card Table的Index。
Young GC 階段:
階段1:根掃描
靜態和本地對象被掃描
階段2:更新RS
處理dirty card隊列更新RS
階段3:處理RS
檢測從年輕代指向年老代的對象
階段4:對象拷貝
拷貝存活的對象到survivor/old區域
階段5:處理引用隊列
軟引用,弱引用,虛引用處理
3:G1 Mix GC
Mix GC不只進行正常的新生代垃圾收集,同時也回收部分後臺掃描線程標記的老年代分區。
它的GC步驟分2步:
全局併發標記(global concurrent marking)
拷貝存活對象(evacuation)
在進行Mix GC以前,會先進行global concurrent marking(全局併發標記)。 global concurrent marking的執行過程是怎樣的呢?
在G1 GC中,它主要是爲Mixed GC提供標記服務的,並非一次GC過程的一個必須環節。global concurrent marking的執行過程分爲五個步驟:
初始標記(initial mark,STW)
在此階段,G1 GC 對根進行標記。該階段與常規的 (STW) 年輕代垃圾回收密切相關。
根區域掃描(root region scan)
G1 GC 在初始標記的存活區掃描對老年代的引用,並標記被引用的對象。該階段與應用程序(非 STW)同時運行,而且只有完成該階段後,才能開始下一次 STW 年輕代垃圾回收。
併發標記(Concurrent Marking)
G1 GC 在整個堆中查找可訪問的(存活的)對象。該階段與應用程序同時運行,能夠被 STW 年輕代垃圾回收中斷
最終標記(Remark,STW)
該階段是 STW 回收,幫助完成標記週期。G1 GC 清空 SATB 緩衝區,跟蹤未被訪問的存活對象,並執行引用處理。
清除垃圾(Cleanup,STW)
在這個最後階段,G1 GC 執行統計和 RSet 淨化的 STW 操做。在統計期間,G1 GC 會識別徹底空閒的區域和可供進行混合垃圾回收的區域。清理階段在將空白區域重置並返回到空閒列表時爲部分併發。
三色標記算法
提到併發標記,咱們不得不瞭解併發標記的三色標記算法。它是描述追蹤式回收器的一種有用的方法,利用它能夠推演回收器的正確性。 首先,咱們將對象分紅三種類型的。
黑色:根對象,或者該對象與它的子對象都被掃描
灰色:對象自己被掃描,但還沒掃描完該對象中的子對象
白色:未被掃描對象,掃描完成全部對象以後,最終爲白色的爲不可達對象,即垃圾對象
當GC開始掃描對象時,按照以下圖步驟進行對象的掃描:
根對象被置爲黑色,子對象被置爲灰色。
繼續由灰色遍歷,將已掃描了子對象的對象置爲黑色。
遍歷了全部可達的對象後,全部可達的對象都變成了黑色。不可達的對象即爲白色,須要被清理。
這看起來很美好,可是若是在標記過程當中,應用程序也在運行,那麼對象的指針就有可能改變。這樣的話,咱們就會遇到一個問題:對象丟失問題
咱們看下面一種狀況,當垃圾收集器掃描到下面狀況時:
這時候應用程序執行了如下操做:
A.c=C
B.c=null
這樣,對象的狀態圖變成以下情形:
這時候垃圾收集器再標記掃描的時候就會下圖成這樣:
很顯然,此時C是白色,被認爲是垃圾須要清理掉,顯然這是不合理的。那麼咱們如何保證應用程序在運行的時候,GC標記的對象不丟失呢?有以下2中可行的方式:
在插入的時候記錄對象
在刪除的時候記錄對象
恰好這對應CMS和G1的2種不一樣實現方式:
在CMS採用的是增量更新(Incremental update),只要在寫屏障(write barrier)裏發現要有一個白對象的引用被賦值到一個黑對象 的字段裏,那就把這個白對象變成灰色的。即插入的時候記錄下來。
在G1中,使用的是STAB(snapshot-at-the-beginning)的方式,刪除的時候記錄全部的對象,它有3個步驟:
1,在開始標記的時候生成一個快照圖標記存活對象
2,在併發標記的時候全部被改變的對象入隊(在write barrier裏把全部舊的引用所指向的對象都變成非白的)
3,可能存在遊離的垃圾,將在下次被收集
這樣,G1到如今能夠知道哪些老的分區可回收垃圾最多。 當全局併發標記完成後,在某個時刻,就開始了Mix GC。這些垃圾回收被稱做「混合式」是由於他們不只僅進行正常的新生代垃圾收集,同時也回收部分後臺掃描線程標記的分區。混合式垃圾收集以下圖:
混合式GC也是採用的複製的清理策略,當GC完成後,會從新釋放空間。