Java物件的生命周期由JVM管理。當程式程式碼中建立了一個物件以後,不必擔心它生命週期的其它部分。JVM將自動查詢那些不再使用的物件,並從堆中回收它們的記憶體。
垃圾收集是JVM的一項主要操作,根據我們的需求進行調整可以為應用程式帶來巨大的效能提升。現代JVM提供了各種垃圾收集演算法。我們需要了解應用程式需要決定使用哪種演算法。
無法在Java中以程式設計方式釋放物件,這不像在C和C++等非GC語言中一樣。因此,不能在Java中使用懸空參照。但是,可能具有空參照(參照指向JVM不會儲存物件的記憶體區域)。每當使用空參照時,JVM都會丟擲NullPointerException
異常。
請注意,由於GC,很少在Java程式中發現記憶體洩漏,但它們確實發生了。我們將在本章末尾建立一個記憶體洩漏。
現代JVM使用以下GC:
上述每個演算法都執行相同的任務 - 查詢不再使用的物件並回收它們在堆中佔用的記憶體。其中有一種比較靠譜的方法是計算每個物件具有的參照數量,並在參照數量變為0
時將其釋放(這也稱為參照計數)。為什麼靠譜? 以迴圈連結串列為例。連結串列的每個節點都有一個對它的參照,但整個物件不是從任何地方參照時,理想情況下它應該被釋放。
JVM不僅可以釋放記憶體,還可以將小記憶體卡盤合併到更大的記憶體中。這樣做是為了防止記憶體碎片。
簡單來說,典型的GC演算法可以執行以下活動 -
GC必須在執行時停止應用程式執行緒。這是因為它在執行時移動物件,因此無法使用這些物件。這種停頓被稱為「世界停頓」,並且在調整我們的GC時,最小化這些停頓的頻率和持續時間是我們的目標。
下面顯示了記憶體合併的範例 -
陰影部分是需要釋放的物件。即使在回收所有空間之後,我們也只能分配最大記憶體等於75Kb
的物件。即使有200Kb
的可用空間,如下所示: