1. 程式人生 > >深入拆解虛擬機器(八)垃圾回收(上)

深入拆解虛擬機器(八)垃圾回收(上)

引用計數法

(1)它的做法是為每個物件新增一個引用計數器,用來統計指向該物件的引用個數。一旦某個物件 的引用計數器為0,則說明該物件已經死亡,便可以被回收了。

(2)具體實現:如果有一個引用,被賦值為某一個物件,那麼該物件的引用計數器+1。如果指向某一個物件的引用,被賦值為其他值,那麼該物件的引用計數器-1。也就是說,我們需要截獲所有的引用更新操作,並且相應地增減目標物件的引用計數器

(3)除了需要額外的空間來儲存計數器,以及繁瑣的更新操作,引用計數法還有一個重大的漏洞,那便是無法處理迴圈引用物件


可達性分析演算法

(1)演算法實質是將一系列GC Roots作為初始的存活物件合集,然後從合集觸發,探索所有能夠被該集合引用到的物件,並且將其加入到集合中,這個過程我們稱之為標記(mark)。最終,未被探索到的物件便是死亡的,是可以被回收的

(2)GC Roots:我們暫時可以理解為由堆外指向堆內的引用,一般而言,GC Roots包括(但不限於)如下幾種:

  • Java方法棧幀中的 區域性變數
  • 已載入類的靜態變數
  • JNI handles
  • 已啟動且未停止的Java執行緒

(3)可達性分析演算法在實踐中還有不少其他問題需要解決,比如說,多執行緒環境下,其他執行緒可能會更新訪問過的物件中的引用,從而造成誤報(將引用設定為null)或者漏報(將引用設定為未被訪問過的物件)

(4)誤報並沒有什麼傷害,Java虛擬機器至多損失部分垃圾回收的機會。漏報比較麻煩,因為垃圾回收器可能回收事實上扔被引用的物件記憶體。一旦原引用訪問已經被回收了的物件,則很有可能會導致Java虛擬機器奔潰


Stop-the-world以及安全點

(1)Java虛擬機器中Stop-the-world是通過安全點機制來實現的。當Java虛擬機器收到Stop-the-world請求,它便會等待所有的執行緒都到達安全點,才允許請求Stop-the-world的執行緒進行獨佔的工作

(2)安全點的初始目的並不是讓其他執行緒停下,而是找到一個穩定的執行狀態。在這個執行狀態下,Java虛擬機器的堆疊不會發生變化。這樣一來,垃圾回收器便能夠’'安全 ''地執行可達性分析


垃圾回收的三種方式
清除

(1)清除,即把死亡物件所佔據的記憶體標記為空閒記憶體,並記錄在一個空閒列表中。當需要新建物件時,記憶體管理模組便會從該空閒記憶體列表中尋找空閒記憶體,並劃分給新建的物件

(2)缺點:①造成記憶體碎片。由於Java虛擬機器堆中物件必須是連續分佈的,因此可能出現總空閒記憶體不夠,但無法分配的極端情況。② 分配效率較低,如果對於一塊連續的記憶體空間,那麼我們可以通過指標加法來做分配。而對於空閒列表,Java虛擬機器則需要逐個訪問列表中的項,來查詢能夠放入新建物件的 空閒記憶體


壓縮

即把存活的物件聚集到記憶體區域的起始位置,從而留下一段連續的記憶體空間。這種做法可以解決記憶體碎片化的問題,但是代價是壓縮演算法的效能開銷


複製

(1)即把記憶體區域分為兩等分,分別用兩個指標from和to來維護,並且只是用from指標指向記憶體區域來分配記憶體。

(2)當發生垃圾回收時,便把存活的物件複製到to指標指向的記憶體區域中,並且交換from指標和to指標的內容。

(3)複製 這種回收方式能夠解決記憶體碎片化的問題,但是它的缺點也及其明顯,極堆空間的使用效率及其低下