大學之道
介紹:
Garbage First(G1)致力於在多CPU和大記憶體伺服器上對垃圾收集提供軟實時目標(soft real-time goal )和高吞吐量(high throughput )。從JDK 6u14開始就已經在Hotspot上試驗,到現在的JDK7依然沒有走出實驗室:
#java -version java version "1.7.0_03" Java(TM) SE Runtime Environment (build 1.7.0_03-b05) Java HotSpot(TM) 64-Bit Server VM (build 22.1-b02, mixed mode) #java -server -XX:+PrintFlagsFinal | grep UseG1GC bool UseG1GC = false {product}
關於region:
在G1中,heap被平均分成若干個大小相等的區域(region)。每個region都有一個關聯的remembered set (RS),RS的資料結構是hash table,裡面的資料是card table (heap中每512byte對映在card table 1byte)。簡單的說RS裡面存在的是region中live objects的指標。當region中資料發生變化時,首先反映到card table中的一個或多個card上,RS通過掃描內部的card table得知region中記憶體使用情況和存活物件。
關於記憶體分配:
由於G1主要關注於多CPU多執行緒,所以記憶體分配採用 thread-local allocation buffers (TLABs)技術。每個分配執行緒都有一個自己的buffers用來分配物件,當buffers用完或者不夠的時候,去重新申請一塊記憶體放在自己的thread-local裡面。這樣物件的記憶體分配被最小化到私有的buffers裡面,緩解了併發分配記憶體的壓力。
當region被填滿後,分配記憶體的執行緒會重新選擇一個新的region。空region被組織到一個linked list裡面,這樣可以快速找到新的region。
對於大物件的分配不是在TLABs進行的,而是在TLABs之外。當一個物件的大小超過region的3/4的時候,這個物件被認為是巨大的(humongous )。巨大的物件被分配到特殊的區域(heap regions )。這些區域只包含巨大物件(humongous object )。
執行過程:
初始標記 :Initial Marking
併發標記 :Concurrent Marking
最終標記 :Final Marking
計數並清理 :Live Data Counting and Cleanup
G1執行的第一階段是初始標記(Initial Marking ),這個階段是STW(Stop the World )的,所有mutator threads將被停止,標記出從GC Root開始直接可達的物件。然後,所有mutator threads將被重啟,進入併發標記(Concurrent Marking )階段。這個階段從GC Root開始對heap中的物件標記,標記執行緒與應用程式執行緒並行直接,耗時較長。當併發標記完成後,開始最終標記(Final Marking )階段。這個階段主要是標記那些在併發標記階段發生變化的物件。同樣最終標記也要STW,但是多個標記執行緒並行執行,很快就可以完成。最後一個階段會對每個區域(region)的回收成本和價值進行排序,根據使用者指定的停頓時間,選擇性的收集某些區域的物件,並統計每個區域物件的數量。
與CMS對比:
總體來說,G1跟CMS一樣,是一塊低延時的收集器,同樣犧牲了吞吐量,不過二者之間得到了很好的權衡。
G1與CMS對比有一下不同:
- 分代: CMS中,堆被分為PermGen,YoungGen,OldGen;而YoungGen又分了兩個survivo區域。在G1中,堆被平均分成幾個區域(region),在每個區域中,雖然也保留了新老代的概念,但是收集器是以整個區域為單位收集的。
- 演算法: 相對於CMS的“標記——清理”演算法,G1會使用壓縮演算法,保證不產生多餘的碎片。收集階段,G1會將某個區域存活的物件拷貝的其他區域,然後將整個區域整個回收。
- 停頓時間可控: 為了縮短停頓時間,G1建立可預存停頓模型,這樣在使用者設定的停頓時間範圍內,G1會選擇適當的區域進行收集,確保停頓時間不超過使用者指定時間。