HotSpot 垃圾收集算法的實現

根據對象存活判定算法和垃圾收集算法,HotSpot 虛擬機上實現這些算法時,對算法的執行效率有嚴格的考量。

一、枚舉根節點

  • 實現:
    • 由于目前的主流 Java 虛擬機使用的都是準確式 GC,所以當執行系統停頓下來后,并不需要一個不漏地檢查完所有執行上下文和全局的引用位置,虛擬機應當是有辦法直接得知哪些地方存放著對象引用。
    • 在 HotSpot 的實現中,是使用一組稱為 OopMap 的數據結構來達到這個目的的,在類加載完成的時候,HotSpot 就把對象內什么偏移量上是什么類型的數據計算出來,在 JIT 編譯過程中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣,GC 在掃描時就可以直接得知這些信息了。
  • 特點:
    • 枚舉根節點時必須會發生 Stop The World

因為分析工作必須在一個能確保一致性的快照中進行,這里“一致性”指的是在整個分析期間整個執行系統看起來就像被凍結在某個時間點上,不可以出現分析過程中對象引用關系不在不斷變化的情況,該點不滿足的話分析結果準確性就無法得到保證。這是 GC 停頓的其中一個重要原因。

二、安全點

  • 實現:
    • 上文提到“在特定的位置”記錄了一些信息,這些位置稱為安全點(Safepoint),即在程序執行時并非在所有地方都停頓下來開始 GC,只有在到達安全點時才能暫停。
    • Safepoint 選定既不能太少也不致于讓 GC 等待時間太長,也不能過于頻繁以致于過分增大運行時的負荷。
    • 對于Safepoint,另一個需要考慮的問題是如何在 GC 發生時讓所有線程(不包括 JNI 調用的線程)都“跑”到最近安全點上再停頓下來。這里有兩種方案:
      • 搶先式中斷(Preemptive Suspension),不需要線程的執行代碼主動去配合,在 GC 發生時,首先把所有線程全部中斷,如果發現有線程中斷的地方不在安全點上,就恢復線程,讓它“跑”到安全點上。現在幾乎沒有虛擬機實現采用搶先式中斷來暫停線程從而響應 GC 事件。
      • 主動式中斷(Voluntary Suspension),需要中斷時不直接對線程操作,僅僅簡單地設置一個標志,各個線程執行時主動去輪詢這個標志,發現中斷標志為真時就自己中斷掛起。輪詢標志的地方和安全點是重合的,另外再加上創建對象需要分配內存的地方。

三、安全區域

?? 使用 Safepoint 似乎已經完美地解決了如何進入 GC 的問題,但實際情況卻并不一定。Safepoint 機制保證了程序執行時,在不太長的時間內就會遇到可進入 GC 的 Safepoint。但是,程序“不執行”的情況呢?所謂的程序不執行就是沒有分配 CPU 時間,典型的例子就是線程處于 Sleep 狀態或者 Blocked 狀態,這時候線程無法響應 JVM 的中斷請求,“走”到完全的地方去中斷掛起,JVM 也顯然不太可能等待線程重新被分配 CPU 時間。對于這種情況,就需要安全區域(Safe Region)來解決。

  • 實現:
    • 安全區域是指在一段代碼片段之中,引用關系不會發生變化。在這個區域中的任意地方開始 GC 都是完全的。我們也可以把 Safe Region 看做是擴展了的 Safepoint。
    • 在線程執行到 Safe Region 中的代碼時,首先標識自己已經進入了 Safe Region,那樣,當在這段時間里 JVM 要發起 GC 時,就不用管標識自己為 Safe Region 狀態的線程了。
    • 在線程要離開 Safe Region 時,它要檢查系統是否已經完成了根節點枚舉(或者整個 GC 過程),如果完成了,那線程就繼續執行,否則它就必須等待直到收到可以完全離開 Safe Region 的信號為止。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容