分代垃圾回收
- 理論支持:經驗得出——"大部分的對象在生成后馬上就變成了垃圾,很少有對象能活得很久"。
- 分代垃圾回收將剛生成的對象稱為新生代,達到一定年齡(進過一次GC即一歲)的對象稱為老年代,不同代的對象使用不同回收算法。
- 新生代對象執行GC稱為新生代GC(minor GC)。
- 新生代對象存活一定次數GC將晉升到老年代,老年代的GC稱為老年代GC(major GC)。
Ungar的分代垃圾回收
准備
- 堆分為生成空間(分配內存的位置)、2個大小相等的幸存空間(From和To)以及老年代空間。
- 記錄集用於記錄老年代指向新生代的引用,為了便於高效的尋找從老年代到新生代的引用。
- 記錄集記錄的是老年代中發出引用的對象
- 記錄集通過寫入屏障在mutator更新對象間的指針操作進行寫入。
- 對象需要增加三個字段:
- age:對象年齡
- forwarded:已經復制完畢的標志
- remembered:已經向記錄集記錄完畢的標志
分配
- 分配是在生成空間進行,其分配算法與復制算法基本一致。
- 當空間不夠將發起minor GC,如果還不足則表示生成空間比新對象小,這種有些直接寫入老年代。
新生代GC
- minor GC把生成空間和From中的對象復制到To或老年代,具體復制目標由對象年齡決定。
- 當對象需要晉升到老年代時,如果老年代空間不夠將發起一次major GC。
- 晉升時不僅要設置forwarded和forwaring,如果該對象還有引用了新生代的對象則需要寫入到記錄集。
- minor GC的根除了正常的GC root還有記錄集中的對象。
老年代GC
老年代GC使用Mark-Sweep算法。
總結
- 改善了GC所花費的時間,提高了吞吐量。
- 其針對的是“很對年輕代對象很快就死了”,如果不符合這個特點可能造成相反的結果。
記錄各代之間引用的方法
卡片標記
- 將老年代按照一定大小分割,分割后的空間稱為卡片,每個卡片准備一個標志位。
- 優點在於節約空間而且不會像記錄集那樣可能會溢出。
- 缺點在於搜索標志位。
頁面標記
- 許多OS以頁面對內存進行管理,將卡片標記法的卡片大小設為頁的大小。
- 當某個頁進行寫入操作時,設置該頁的重寫標志位,從而達到標記。
- 缺點:並不是所有OS都支持頁的模式。
改進
多代垃圾回收
- 增加代數,給每代記錄一個記錄集。
- 並不是代數越多越好,需要根據實際情況判斷。
列車垃圾回收
- 目的在於控制老年代GC的最大停頓時間。
- 堆划分為一個個車廂,1個以上的車廂連接構成列車,1次老年代GC是以1個車廂作為GC對象。
- 每個車廂和每個列車均有一個對應的記錄集,記錄來自其他車廂或列車的引用。
- 新生代GC將GC root和記錄集中引用的對象復制到老年代,其中GC root引用相關對象將復制到一個空閑車廂,而記錄集中的對象則復制到老年代發起引用的車廂對應列車的尾車廂中。
- 老年代GC以列車開頭車廂作為GC對象,將該車廂對象復制與年輕代復制的目標選擇一樣。
- 寫入屏障,老年代指向新生代則寫入新生代記錄集,老年代指向老年代則如果指針目標對象所在的車廂在指針對象所做車廂之前需要記錄到指針目標對象的記錄集中(原因在於按順序回收車廂)