分代收集演算法
- 1.分代回收演算法概述
- 1.1.分代回收演算法簡介
- 1.2.回收流程
- 1.3.回收流程總結
- 2.相關VM引數
- 3.GC分析
1.分代回收演算法概述
1.1.分代回收演算法簡介
我們在前面講了三種垃圾回收演算法,實際上,虛擬機結合這三種演算法,讓它們協同作業,具體的實作使用分代垃圾回識訓制,

我們將堆記憶體區域劃分為新生代和老年代,
新生代中又劃分為伊甸園、幸存區From、幸存區To,
因為Java中有的物件需要長時間使用,長時間使用的物件,我們將之放在老年代中,而那些用完了就可以丟棄的物件,我們將之放在新生代中,這樣我們就可以根據物件生命周期的不同特點進行不同的垃圾回收策略,老年代的垃圾回收很久才一次,新生代的垃圾回收則比較頻繁,針對不同的區域我們采用不同的演算法來對我們的垃圾回收進行一個管理,
我們可以做一個形象的比喻,比如保潔工人而言,垃圾箱每天清理一次,一棟樓的話我們清理的次數可能就少一些,
1.2.回收流程
新創建的物件都被放在了新生代的伊甸園中
伊甸園是一個很形象的比喻,是西方國家神話傳說中人類始祖亞當夏娃誕生的地方,

當伊甸園中的記憶體不足時,就會進行一次垃圾回收,這時的回收叫做 Minor GC
Minor:小的動作
Minor采用可達性分析演算法,沿著GCRoot鏈查找,看這些物件是有用還是作為垃圾,
Minor GC 會將伊甸園和幸存區FROM存活的物件先復制到 幸存區 TO中, 并讓其壽命加1,再交換兩個幸存區



再次創建物件,若新生代的伊甸園又滿了,則會再次觸發 Minor GC(會觸發 stop the world, 暫停其他用戶執行緒,只讓垃圾回收執行緒作業),這時不僅會回收伊甸園中的垃圾,還會回收幸存區中的垃圾,再將活躍物件復制到幸存區TO中,回收以后會交換兩個幸存區,并讓幸存區中的物件壽命加1


如果幸存區中的物件的壽命超過某個閾值(最大為15,4bit),就會被晉升老年代中

如果新生代老年代中的記憶體都滿了,就會先觸發Minor Gc,再觸發Full GC,掃描新生代和老年代中所有不再使用的物件并回收
Full GC當老年代記憶體不足,即老年代的垃圾回收,
1.3.回收流程總結
物件首先分配在伊甸園區域
新生代空間不足時,觸發minor gc,伊甸園和from存活的物件使用copy賦值到to中,存活的物件年齡加1并且交換from to
minor gc會引發stop the world(即在發生垃圾回收的時候,必須暫停其他的用戶執行緒,當垃圾回收執行緒的任務做完了,其它執行緒才能運行,因為牽扯到記憶體的復制).暫停其它用戶執行緒,等垃圾回收結束,用戶執行緒才恢復運行,
當物件壽命超過閾值時,會晉升至老年代,最大壽命是15(4bit),(但是在新生代記憶體緊張時可能會變小)
當老年代空間不足,會先嘗試觸發minor gc,如果之后空間仍不足,那么會觸發full gc,STW的時間更長(因為老年代的存活數量比較多),
如果老年代的記憶體還是不足,就會觸發堆記憶體溢位,
2.相關VM引數
| 含義 | 引數 |
|---|---|
| 堆初始大小 | -Xms |
| 堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size |
| 新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) |
| 幸存區比例(動態) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy |
| 幸存區比例 | -XX:SurvivorRatio=ratio |
| 晉升閾值 | -XX:MaxTenuringThreshold=threshold |
| 晉升詳情 | -XX:+PrintTenuringDistribution |
| GC詳情 | -XX:+PrintGCDetails -verbose:gc |
| FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC |
3.GC分析
/**
* 演示記憶體的分配策略
*/
public class Demo2_1 {
private static final int _512KB = 512 * 1024;
private static final int _1MB = 1024 * 1024;
private static final int _6MB = 6 * 1024 * 1024;
private static final int _7MB = 7 * 1024 * 1024;
private static final int _8MB = 8 * 1024 * 1024;
//堆初始20M,最大20M,新生代10M,UseSerialGC垃圾回收器,最后兩個引數列印詳細
// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
public static void main(String[] args) throws InterruptedException {
}
}

new generation新生代,tenured generation老年代 Meta元空間,它并不屬于堆的一部分,
新生區這里有9M,8M劃分給了伊甸園,幸存區from和to各占用1M,幸存區to要始終空著,所以計算空間時把1M給去掉了,
老年代沒有被使用,
private static final int _512KB = 512 * 1024;
private static final int _1MB = 1024 * 1024;
private static final int _6MB = 6 * 1024 * 1024;
private static final int _7MB = 7 * 1024 * 1024;
private static final int _8MB = 8 * 1024 * 1024;
// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
public static void main(String[] args) throws InterruptedException {
ArrayList<byte[]> list = new ArrayList<>();
list.add(new byte[_7MB]);
list.add(new byte[_512KB]);
list.add(new byte[_512KB]);
}

大物件處理策略
當遇到一個較大的物件時,就算新生代的伊甸園為空,也無法容納該物件時,會將該物件直接晉升為老年代
private static final int _512KB = 512 * 1024;
private static final int _1MB = 1024 * 1024;
private static final int _6MB = 6 * 1024 * 1024;
private static final int _7MB = 7 * 1024 * 1024;
private static final int _8MB = 8 * 1024 * 1024;
// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
public static void main(String[] args) throws InterruptedException {
ArrayList<byte[]> list = new ArrayList<>();
list.add(new byte[_8MB]);
}

執行緒記憶體溢位
某個執行緒的記憶體溢位了而拋例外(out of memory),不會讓其他的執行緒結束運行
這是因為當一個執行緒拋出OOM例外后,它所占據的記憶體資源會全部被釋放掉,從而不會影響其他執行緒的運行,行程依然正常
public class Demo2_1 {
private static final int _512KB = 512 * 1024;
private static final int _1MB = 1024 * 1024;
private static final int _6MB = 6 * 1024 * 1024;
private static final int _7MB = 7 * 1024 * 1024;
private static final int _8MB = 8 * 1024 * 1024;
// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
ArrayList<byte[]> list = new ArrayList<>();
list.add(new byte[_8MB]);
list.add(new byte[_8MB]);
}).start();
System.out.println("sleep....");
Thread.sleep(1000L);
}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255318.html
標籤:其他
