jvm垃圾收集器詳解
阿新 • • 發佈:2021-10-28
1. 簡介
垃圾收集器雖然從Serial -> Parallel->ParNew->CMS->G1->ZGC一步步演化,但是新的垃圾收集器不一定就適合所有場景,從垃圾收集的發展程序的可以看出,垃圾收集器的演化無非因為一下幾點:
- 貼合當下的硬體,能夠更友好的使用硬體資源
- 減少STW(Stop The World)的時間,提高使用者體驗
2. 基本概念
2.1 jvm記憶體模型
- 本地方法棧: native方法
- 棧:一個方法對應一塊棧幀記憶體空間,主要儲存區域性變量表、運算元棧、動態連結、方法出口
- 程式計數器:每一個棧都有自己的程式計數器,使用者恢復現場
- 堆:記憶體分配和垃圾回收的主要場所
- 方法區:執行時常量池,主要儲存元空間、常量、靜態變數、類資訊
2.2 物件記憶體分配流程
- 物件逃逸分析:當一個物件在方法中被定義後,沒有被外部方法引用
- 方法內部的物件,可以直接分配在棧的,但是太大了也只能放在堆中
- 如果物件不會逃逸可以將該物件在棧上分配記憶體,這樣該物件所佔用的記憶體空間就可以隨棧幀出棧而銷燬,就減輕了垃圾回收的壓力
- 棧上分配依賴於逃逸分析和標量替換
- 標量替換:標量指不可被進一步分解的量,而JAVA的基本資料型別就是標量
- 確認物件不逃逸,將物件的成員變數拆分成若干個被這個方法使用的成員變數替換
2.2 垃圾查詢演算法
- 引用計數法: 效率高,無法解決迴圈引用的問題(基本不用)
- 可達性分析法:將“GC Roots” 物件作為起點,從這些節點開始向下搜尋引用的物件,找到的物件都標記為非垃圾物件,其餘未標記的 物件都是垃圾物件
- 根節點(root)
- 執行緒棧的本地變數
- 靜態變數
- 本地方法棧的變數
2.3 垃圾收集演算法
- 分代收集理論
- 根據存活週期不同將記憶體分為幾塊
- 新生代每次收集都會有大量的物件死亡(一般採用複製演算法)
- 老年代的物件存活的機率比較大(一般採用標記整理或者標記清除)
- 複製演算法:將記憶體分為大小相同的兩塊,每次使用一塊,當一塊記憶體使用完後,就將還存活的物件複製到另一塊,清理掉當前的塊記憶體
- 優點:不會產生記憶體碎片,效率高
- 缺點:記憶體被分割為兩份,佔用記憶體較多
- 標記清除演算法:標記存活的物件,清除沒有被標記的物件(會造成大量的記憶體碎片,最後肯定會進行一次整理演算法)
- 優點:記憶體佔用低
- 缺點:效率不高,容易使記憶體碎片化
- 標記整理演算法:標記存活的物件,讓所有存活的物件向一端移動,然後直接清理掉端邊界以外的記憶體
- 優點:記憶體佔用低
- 缺點:效率不高,比清除演算法還要低,清除演算法會將多次碎片記憶體進行一次整理
3. 垃圾收集器
- 黑色相連: 表示可以相互配合使用
- 紅色相連:表示在最後可能會轉變成另外一種方式回收