首頁 > 軟體

Java GC垃圾回收演演算法分析

2022-12-21 14:01:26

物件探活

在討論回收演演算法前,更為重要的問題是如何判斷一個物件是否可以被回收?

參照計數演演算法

每個物件會維護一個count,當有一個物件的屬性參照自己時,count自增。當為0時,意味可被回收。

缺點:

  • 需要頻繁執行維護count的操作
  • 無法解決迴圈依賴問題。比如下圖中的D與E節點,已經沒有其他物件的內部在參照時,應該被判定為可回收物件。但是由於雙方相互參照,形成了迴圈依賴,無法被回收。

可達性分析(目前主流虛擬機器器垃圾回收器採取的演演算法):

將符合的GC Roots作為初始的存活對集合,以該集合中的Roots為起點,探索所有能夠被Roots參照到的物件,並加入到Roots集合中,這個過程稱之為標記。未被探索到的物件即是可回收物件(死亡的)。

優點:可以解決參照計數演演算法的迴圈依賴問題。從GC Roots出發,無法探測到迴圈依賴的物件,那麼就會進行回收。

那麼什麼樣的物件可以被作為Root物件(包括但不限於)

  • 區域性變數表中的物件參照
  • 已被載入的靜態屬性參照的物件
  • 常數物件參照

強-軟-弱-虛參照

有些時候,我們有這樣一種需求,當記憶體足夠時,會保留一些物件,方便後續呼叫。當記憶體不足時,將這些物件回收,留出更多的記憶體空間。系統的很多快取功能符合上述條件。

// 強參照:只要參照可達,就永遠不會被回收
Object obj = new Object();
// 軟參照:堆記憶體不夠時被回收
SoftReference<Object> softReference = new SoftReference<>(obj);
// 弱參照:只要觸發GC就會被回收
WeakReference<Object> weakReference = new WeakReference<>(obj);
/**
 * 虛參照:虛參照不會決定物件的生命週期,如果一個物件持有虛參照,那麼和沒有參照一樣,get永遠返回null。
 *
 * 需要配合參照佇列使用,當垃圾回收器準備回收一個虛參照時,會將其加入到參照佇列中。
 *
 * 程式可以根據虛參照是否入隊,來了解物件是否即將被垃圾回收,進而執行一些響應操作。
 */
Object obj2 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj2, referenceQueue);
System.out.println(phantomReference.isEnqueued()); // false
obj2 = null;
System.gc();
Thread.sleep(500);
System.out.println(phantomReference.isEnqueued()); // true

標記清除

  • 根據可達性分析,標記可以回收的物件
  • 將被標記的可回收物件進行回收

記憶體碎片問題:造成了不連續的記憶體碎片。當有大物件需要儲存時,若連續的碎片空間儲存不下, 難免會再次觸發垃圾回收的操作。

標記複製

  • 保留一半的空間,每次只使用一半的空間。
  • 每次GC,根據可達性分析,將存活物件複製到另一片空間。
  • 釋放本次使用一半空間。

優點:

  • 只需針對一半的空間進行回收。
  • 避免了記憶體碎片問題。
  • 只需要按順序分配記憶體空間即可,實現簡單。

缺點:

  • 可使用的記憶體空間縮減了一半。
  • 執行時需要STW,導致掛起執行的使用者執行緒

標記整理

標記清除的改進

  • 根據可達性分析,對可以回收的物件進行標記
  • 將未被標記的存活物件移動到另一端,然後直接清理掉端邊界以外的記憶體

優點:解決了標記清除的記憶體碎片問題

缺點:

  • 相比標記清除的直接釋放,需要更多的時間來完成整理部分的操作
  • 執行時需要STW,導致掛起執行的使用者執行緒

回收演演算法的在堆記憶體上的應用

新生代

根據新生代的特點,物件存活率較低,應用標記複製演演算法。分配記憶體空間時,使用Eden區與一塊Survivor區,GC後將存活的物件放入到另一塊Survivor區。如果另一塊Survivor區不夠存放存活物件,多數情況下會使用老年代進行分配擔保(分配擔保:將無法儲存的存活物件放入其他儲存空間)

迴圈:

Eden + S0 -> S1 (將Eden 與 S0存活的物件複製到S1)

Eden + S1 -> S0

Eden + S0 -> S1

老年代

根據老年代的特點,物件存活率較高,一般用標記-清除,標記-整理演演算法。

到此這篇關於Java GC垃圾回收演演算法分析的文章就介紹到這了,更多相關Java GC垃圾回收內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com