一、起源 1960年Lisp語言: 第一門真正使用記憶體動態分配和垃圾回收的語言。 二、概要 線程相關:程式計數器、虛擬機棧、本地方法棧,不需要考慮垃圾回收 Java堆、方法區:需要考慮垃圾回收 三、垃圾回收演算法 1 引用計數演算法 2 可達性分析演算法 垃圾收集演算法 1 標記-清除演算法 Mark-Swe ...
一、起源
1960年Lisp語言: 第一門真正使用記憶體動態分配和垃圾回收的語言。
二、概要
- 線程相關:程式計數器、虛擬機棧、本地方法棧,不需要考慮垃圾回收
- Java堆、方法區:需要考慮垃圾回收
三、垃圾回收演算法
1 引用計數演算法
2 可達性分析演算法
垃圾收集演算法
1 標記-清除演算法
Mark-Sweep
最基礎的垃圾收集演算法
不足
- 效率問題:標記和清除的過程效率不太高
- 空間問題:標記清除之後產生大量的碎片,再分配較大的對象時,由於空間不足不得不再進行另一次GC操作。
2 複製演算法
Copying
- 半個區的記憶體回收後挪到另外半個區
- 只需要移動堆頂指針,按順序分配記憶體。
不足
每次都只能使用半個區的記憶體。
重點
現在的商業虛擬機都採用複製演算法回收新生代。
新生代98%的對象都是朝生夕死的,所以不需要1:1比例劃分記憶體空間。
分為一個Eden區,兩個survivor區
HotSpot預設記憶體 8:1 比例,只有10%記憶體被浪費。
Survivor區不夠用老年代。
3 標記-整理演算法
Mark-Compact
原理
如果複製演算法不想浪費50%的記憶體空間,就要有額外的空間擔保Survivor區。用來應對所有對象都存活的情況。
所以老年代不能用複製演算法。
流程
先標記,然後不直接清理,而是所有存活對象向一端移動。清理掉端邊界以外的記憶體。
四、分代收集演算法
Generational Collection
五、HotSpot 演算法實現
枚舉根節點
可達性分析
GC Root:全局性引用(常量、類靜態屬性)、執行上下文(棧幀的本地變數表)
GC停頓:分析工作必須在一個能確保一致性的快照中進行。所以GC時必須停頓所有Java線程。
準確式GC
不需要檢查所有的執行上下文和全局的引用位置。
HotSpot使用OopMap在類載入完成時,計算對象內什麼偏移量是什麼類型。JIT編譯過程中記錄棧和寄存器哪些位置是哪些引用。
安全點
不為所有指令都生成OopMap(占用空間大),只在特定的位置生成--安全點(Safe Point)
安全點選定標準
是否具有讓程式長時間執行的特征--指令復用,如方法調用、迴圈跳轉、異常跳轉。
所有線程都跑到安全點停頓(不包括JNI線程)
搶先式中斷(Preemptive Suspension)
- 線程先全部中斷,有不在安全點上的就恢復,跑到安全點上再中斷。
- 現在沒有虛擬機這麼實現。
主動式中斷(Voluntary Suspension)
- 設置一個標誌,在輪詢標誌的時候發現標誌是真就掛起。
- 輪詢標誌的位置:SafePoint;創建對象需要分配記憶體的位置。
安全區
背景
1. 沒有分配CPU時間的時候執行不到安全點。
2. 線程處於Sleep或Blocked狀態
原理
1. 一段代碼片段中,引用關係不會發生變化,在這個區域中的任意地方開始GC都是安全的。
2. 線程進入Safe Region時標識自己進入SafeRegion
六、垃圾收集器
新生代垃圾收集器
Serial
ParNew
Parallel Scavenge