5.垃圾回收器
常用組合:
1.Serial和Serial Old Serial 使用單執行緒進行垃圾回收,並且會停止所有工作執行緒(STW),停頓時間長,故現在用的很少(用於回收新生代) 。Serial Old 用於老年代,使用標記清理演算法,也是單執行緒。
2.Parallel Scavenge和 Parallel Old
若JVM沒有做任何調優,就是預設的這一組。PS 使用多執行緒清理垃圾,用於新生代。 PO使用整理演算法。 3.ParNew和CMS
ParNew 跟PS沒有區別
CMS: 1.基於"標記-清除"演算法(不進行壓縮操作,產生記憶體碎片); 2.以獲取最短回收停頓時間為目標; 3.併發收集、低停頓; 是HotSpot在JDK1.5推出的第一款真正意義上的併發(Concurrent)收集器;第一次實現了讓垃圾收集執行緒與使用者執行緒(基本上)同時工作; CMS產生的階段: 1.初始標記STW開始標記。 2.併發標記和應用程式同時執行。 3.重新標記STW,在併發標記中產生的新垃圾在重新標記. 4.併發清理。CMS缺點:1.產生記憶體碎片. 2.產生浮動垃圾。在併發清理中還會產生垃圾。
4.G1
G1是一種服務端應用使用的垃圾回收器,目標是用在多核,大記憶體的機器上,它在大多數情況上可以實現指定的GC暫停世界,同時還能保持較高的吞吐量.
G1將記憶體劃分成了多個大小相等的Region(預設是512K),Region邏輯上連續,實體記憶體地址不連續。同時每個Region被標記成E、S、O、H,分別表示Eden、Survivor、Old、Humongous。其中E、S屬於年輕代,O與H屬於老年代。
H表示Humongous。從字面上就可以理解表示大的物件(下面簡稱H物件)。 當分配的物件大於等於Region大小的一半的時候就會被認為是巨型物件。H物件預設分配在老年代,可以防止GC的時候大物件的記憶體拷貝。通過如果發現堆記憶體容不下H物件的時候,會觸發一次GC操作。 在進行Young GC的時候,Young區的物件可能還存在Old區的引用, 這就是跨代引用的問題。 為了解決Young GC的時候掃描整個老年代,G1引入了Card Table
和Remember Set
的概念,基本思想就是用空間換時間。這兩個資料結構是專門用來處理Old區到Young區的引用。Young區到Old區的引用則不需要單獨處理,因為Young區中的物件本身變化比較大,沒必要浪費空間去記錄下來。
RSet:全稱Remembered Sets, 用來記錄外部指向本Region的所有引用,每個Region維護一個RSet。RSet的價值在於使得垃圾回收器不需要掃描整個堆 找到誰引用了當前分割槽中的物件,只需要掃描RSet即可.
Card Table:如果Old區中的物件指向了Young區,就將它設為Dirty(髒的),下次掃描時,只需要掃描Dirty Card,在結果上Card Table使用了Bitmap.
4.1 G1 GC主要可以分為兩個階段4.1.1 全域性併發標記(global concurrent marking) 全域性併發標記又可以進一步細分成下面幾個步驟:
- 初始標記(initial mark,STW)。它標記了從GC Root開始直接可達的物件。初始標記階段借用young GC的暫停,因而沒有額外的、單獨的暫停階段。
- 併發標記(Concurrent Marking)。這個階段從GC Root開始對heap中的物件標記,標記執行緒與應用程式執行緒並行執行,並且收集各個Region的存活物件資訊。過程中還會掃描上文中提到的SATB write barrier所記錄下的引用。
- 最終標記(Remark,STW)。標記那些在併發標記階段發生變化的物件,將被回收。
- 清除垃圾(Cleanup,部分STW)。這個階段如果發現完全沒有活物件的region就會將其整體回收到可分配region列表中。 清除空Region。
漏標:當黑色物件A指向白色物件D,灰色物件B指向白色沒了.如下圖,這樣會產生漏標,遍歷不到。必須具備兩個兩個條件:黑色指向白色物件,灰色指向白色物件的引用消失.
CMS和G1的核心就在於併發標記的執行緒和工作執行緒同時進行,只有這個階段會產生漏標.
兩種解決方案:
1.增量更新,關注引用的增加,把黑色A物件變成灰色,下次需要重新掃描屬性.(CMS使用).
2.STAB 關注引用的刪除。當B指向D的引用消失時,把這個引用推到GC的堆疊,保證D還能被掃描到,下次掃描直接能掃白色,不會產生漏標(G1使用).
當灰色到白色引用消失時,由於有RSet存在,不需要掃描整個堆去查詢指向白色的引用,效率比較高.