Java虛擬機器(JVM)垃圾收集


Java物件的生命周期由JVM管理。當程式程式碼中建立了一個物件以後,不必擔心它生命週期的其它部分。JVM將自動查詢那些不再使用的物件,並從堆中回收它們的記憶體。

垃圾收集是JVM的一項主要操作,根據我們的需求進行調整可以為應用程式帶來巨大的效能提升。現代JVM提供了各種垃圾收集演算法。我們需要了解應用程式需要決定使用哪種演算法。

無法在Java中以程式設計方式釋放物件,這不像在C和C++等非GC語言中一樣。因此,不能在Java中使用懸空參照。但是,可能具有空參照(參照指向JVM不會儲存物件的記憶體區域)。每當使用空參照時,JVM都會丟擲NullPointerException異常。

請注意,由於GC,很少在Java程式中發現記憶體洩漏,但它們確實發生了。我們將在本章末尾建立一個記憶體洩漏。

現代JVM使用以下GC:

  • 序列收集器
  • 吞吐量收集器
  • CMS收集器
  • G1收集器

上述每個演算法都執行相同的任務 - 查詢不再使用的物件並回收它們在堆中佔用的記憶體。其中有一種比較靠譜的方法是計算每個物件具有的參照數量,並在參照數量變為0時將其釋放(這也稱為參照計數)。為什麼靠譜? 以迴圈連結串列為例。連結串列的每個節點都有一個對它的參照,但整個物件不是從任何地方參照時,理想情況下它應該被釋放。

JVM不僅可以釋放記憶體,還可以將小記憶體卡盤合併到更大的記憶體中。這樣做是為了防止記憶體碎片。

簡單來說,典型的GC演算法可以執行以下活動 -

  • 查詢未使用的物件;
  • 釋放它們在堆中佔用的記憶體;
  • 合併碎片;

GC必須在執行時停止應用程式執行緒。這是因為它在執行時移動物件,因此無法使用這些物件。這種停頓被稱為「世界停頓」,並且在調整我們的GC時,最小化這些停頓的頻率和持續時間是我們的目標。

記憶體合併

下面顯示了記憶體合併的範例 -

內存合併

陰影部分是需要釋放的物件。即使在回收所有空間之後,我們也只能分配最大記憶體等於75Kb的物件。即使有200Kb的可用空間,如下所示:

內存合併2