JVM運作資料區
Java在執行Java程式的過程中會把所管理的記憶體劃分為為若幹個不同的資料區域,如下圖
JDK1.8之前方法區由永久代實作(永久代在堆中)
JDK1.8方法區由元空間實作(元空間在本地記憶體)
JDK1.8之前堆記憶體被分為新生代,老年代,永久代
JDK1.8之後堆記憶體被分為新生代,老年代,預設比例為1:2,新生代又被分為1個Eden區+2個Survivor區(S0區,S1區),其中Eden區,S0區,S1區的預設比例為8:1:1
堆和方法區是所有線程共享的資料區
虛拟機棧,本地方法棧,程式計數器是線程隔離的資料區
判斷對象是否已死
- 引用計數法(JVM沒有采用這種,因為互相引用,會導緻回收不了)
- 可達性算法分析
标記-清除算法
先标記出所有需要回收的對象,在标記完成後統一回收被标記的對象。也可以反過來,标記存活的對象,統一回收未被标記的對象
缺點
- 效率問題,标記和清除的兩個過程的效率都不高
- 空間問題,标記清除之後會産生大量不連續的記憶體碎片,空間碎片太多可能會導緻以後再程式運作過程中需要配置設定較大對象時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。
标記-複制算法
将可用記憶體按容量劃分為大小相等的兩塊,每次隻使用其中的一塊。當這一塊的記憶體用完了,就将還存活的對象複制到另一塊上面,然後再把已使用過的記憶體空間一次清理掉。
優點
這樣使得每次都是對整個半區進行記憶體回收,記憶體配置設定時也就不用考慮記憶體碎片等複雜情況,隻要移動堆頂指針,按順序配置設定記憶體即可,實作簡單,運作高效。
缺點
将記憶體縮小為原來的一半
改善
将新生代分為一塊較大的Eden空間和兩塊較小的Survivor空間(S0和S1)。每次配置設定記憶體時隻使用Eden和其中一塊Survivor。發生垃圾收集時,将Eden和Survivor中仍然存活的對象一次性複制到另外一塊Survivor上,接着清理掉Eden和已經用過的Survivor空間,循環往複。當Survivor空間不足以容納一次Minor GC之後存活的對象時,就需要依賴其他記憶體區域(大多就是老年代)進行配置設定擔保
循環往複的過程如下
Eden+S0->S1(将Eden和S0存活的對象移動到S1)
Eden+S1->S0
Eden+S0->S1