深入虛擬機二(垃圾收集算法和收集器)

垃圾收集算法

標記-清除算法

    首先標記所有需要回收的對象,在標記完成後統一回收所有被標註的對象。標記就是前一章所說的(1-引用計數法 2-可達性分析法)。他是最基礎的算法,後續的算法都是根據這種思路並對其不足改進而得到的。缺點:
1- 效率太低(標記和清除兩個過程效率都不高)
2- 空間問題:標記清除後會導致大量不連續的空間碎片,碎片太多會導致在分配大對象時無法找到足夠的空間而不得不提前觸發一次gc

在這裏插入圖片描述

複製算法

    對內存劃分爲兩個區域,每次使用一塊。當一塊內存用完了就將還存活的複製到另一塊,把已使用的內存空間一次性清理掉。這樣解決了空間碎片問題,因爲只要移動堆頂指針,按順序分配內存即可。但這樣是以縮小內存爲代價因爲新生代的對象98%都是‘朝生夕死’的,Hostpot虛擬機默認eden和survivor的比例是8:1。這樣只有10%的空間會浪費。當然我們沒有辦法保證每次回收只有不都10%的對象存活,當survivor空間不夠用的時候,就需要老年代進行分配擔保。
在這裏插入圖片描述

標記-整理算法

    因爲新生代存活率很低所以適合複製算法,但老年代的存活率很高如果還是用那種算法會導致大量的內存浪費。所以根據老年代,基本上是使用“標記-整理”。過程和標記-清除算法一樣,但後續步驟不是直接對可回收的對象進行清理,所有存活的對象都移向一端,然後清除端邊界以外的內存
在這裏插入圖片描述

分代收集算法

    商業虛擬機都是採用該算法,這個算法也不是什麼新思想。根據內存的存活週期不同將內存劃分爲幾塊。一般堆分爲新生代和老年代,根據各個年代採用上面所講的最適當的算法

hotspot的算法實現

  • 枚舉根節點 更多

        GC Roots的一個枚舉,可作爲GC Roots的節點。確保一致性的快照(在整個分析期間執行系統看起來就像被凍結在某個時間點,不可以在分析過程中對象引用關係還在不斷的變化)。因爲爲了保證一致性會導致GC必須停頓所有java執行線程(“Stop The World”)。當系統停頓後並不需要一個不漏的檢查完所有執行上下文和全局的引用位置。虛擬機應該有辦法直接知道哪些方法 存放着對象引用
    在HotSpot中,使用一組OopMap的數據結構來標記對象引用的位置。
    1- 在類加載完成的時候,HotSpot就把對象內什麼偏移量上是什麼類型的數據計算出來。
    2- 在JIT編譯過程中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。
    3- 在OopMap的協助下,HotSpot可以快速且準確地完成GC Roots枚舉。

  • 安全點

        在OopMap的協助下可以快速準確的完成GC Root枚舉。但是OopMap內容變化的指令非常多,如果爲每一個指令都生成對應的OopMap,將會需要大量的額外空間,這樣GC的空間成本也會非常高。實際上HotSpot的確,沒有爲每一條指令都去生成OopMap,他只是會在"特定的位置"記錄這些信息,這些位置稱爲安全點。程序只有到達安全點才能暫停開始GC.。所以safepoint既不能太少以至於讓GC等待的時間太長,過多會增大運行時負荷
        safeppoint選定標準:是否具有讓程序長時間執行的特徵
        特徵:指令序列的複用,例如方法調用、循環跳轉、異常跳轉等
    對於safeppoint需要考慮如何在GC發生時讓所有的線程都跑到最近的安全點停頓,這裏有兩種方案可供選擇:搶斷式中斷(Preemptive Suspension)和主動式中斷(Voluntary Suspension)


搶斷式中斷:不需要執行代碼的主動配合,首先把所有的線程全部中斷,如果發現有線程中斷的地方不再安全點,就恢復線程讓他跑到安全點。現在幾乎沒有虛擬機採用該方式中斷線程從而響應GC事件
主動式中斷:當GC需要中斷線程之後,不直接對線程操作,僅僅簡單地設置一個標誌,各各線程執行的時候主動去輪詢這個標誌,發現中斷標誌位真時就自己中斷掛起。輪詢標誌的地方和安全點是重合的,另外在加上創建對象需要分配內存的地方。

  • 安全區域

    safeppoint似乎已經完美的解決了如何進入GC的問題,但實際情況卻不一定,safeppoint機制保證程序時,在不太長的時間內就會遇到可進入GC的safepoint。但是程序不執行的時候?不執行就是執行的時候 沒有分配CPU時間,典型的例子就是線程處於Sleep狀態或者Blocked狀態。這是的線程無法響應JVM的中斷請求走到安全的地方去中斷掛起。JVM顯然也不可能等待線程重新被分配CPU時間。這種情況就需要安全區域(Safe Region) 來解決安全是指一段代碼片段之中,引用關係不會發生變化。這個區域任意地方開始GC都是安全。我們可以把Safe Region看做是被擴展了的safepoint。在線程執行到Safe Region中的代碼時首先會將自己標識,那樣當JVM要發起GC時,就不用管標識自己爲Safe Region狀態的線程了。在線程要離開時,他會檢查系統是否完成了根節點枚舉(也就是整個GC過程),如果完成了就繼續執行,否則他就必須等待直到收到可以安全離開Safe Region的信號爲止。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章