JVM記憶體模型與垃圾回收
一、JVM體系結構
二、JVM Heap Memory
1.新生代(Young Generation)
- Eden Space
- Survivor FromSpace (S1)
- Survivor ToSpace (S2)
備註: Young Generation中的98%的物件都是死朝生夕死,所以將記憶體分為一塊較大的Eden和兩塊較小的Survivor1、Survivor2,JVM預設分配是8:1:1。
2.老年代(Old Generation)
- Tenured
3.永久代(Permanent Generation)
- 包含類、方法等細節的元資訊
- 在Java8中已被移除
三、垃圾回收過程
新生代GC(Minor GC):
垃圾回收演算法:Copying
1.一個新建立的物件,首先會被儲存在新生代的Eden 中,Eden空間不足時,GC,將存活的物件(仍然被引用的)從 Eden 移動到 Survivor1;
2.Survivor1空間不足時,GC,將Eden和Survivor1中存活的物件,移動到Survivor2,如果Survivor2空間不足,則全部移動到老年代;
3.Survivor2空間不足時,GC,將Eden和Survivor1中存活的物件,移動到Survivor1,如果Survivor1空間不足,則全部移動到老年代;
老年代GC(Major GC / Full GC):
垃圾回收演算法:Mark-Compact
相對於 Java 垃圾回收過程,老年代是物件生命週期的最後階段。Major GC 掃描老年代的垃圾回收過程。如果例項不再被引用,那麼它們會被標記為回收,否則它們會繼續留在老年代中。
四、垃圾回收演算法
①Mark-Sweep(標記-清除)演算法
這是最基礎的垃圾回收演算法,之所以說它是最基礎的是因為它最容易實現,思想也是最簡單的。標記-清除演算法分為兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的物件,清除階段就是回收被標記的物件所佔用的空間。具體過程如下圖所示:
從圖中可以很容易看出標記-清除演算法實現起來比較容易,但是有一個比較嚴重的問題就是容易產生記憶體碎片,碎片太多可能會導致後續過程中需要為大物件分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。
②Copying(複製)演算法
為了解決Mark-Sweep演算法的缺陷,Copying演算法就被提了出來。它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的物件複製到另外一塊上面,然後再把已使用的記憶體空間一次清理掉,這樣一來就不容易出現記憶體碎片的問題。具體過程如下圖所示:
這種演算法雖然實現簡單,執行高效且不容易產生記憶體碎片,但是卻對記憶體空間的使用做出了高昂的代價,因為能夠使用的記憶體縮減到原來的一半。很顯然,Copying演算法的效率跟存活物件的數目多少有很大的關係,如果存活物件很多,那麼Copying演算法的效率將會大大降低。我們的新生代GC演算法採用的是這種演算法
③Mark-Compact(標記-整理)演算法
為了解決Copying演算法的缺陷,充分利用記憶體空間,提出了Mark-Compact演算法。該演算法標記階段和Mark-Sweep一樣,但是在完成標記之後,它不是直接清理可回收物件,而是將存活物件都向一端移動,然後清理掉端邊界以外的記憶體。具體過程如下圖所示:
在一般廠商JVM中老年代GC就是使用的這種演算法,由於老年代的特點是每次回收都只回收少量物件。
五、垃圾回收器
1.序列垃圾回收器(Serial Garbage Collector)
序列垃圾回收器通過持有應用程式所有的執行緒進行工作。它為單執行緒環境設計,只使用一個單獨的執行緒進行垃圾回收,通過凍結所有應用程式執行緒進行工作,所以可能不適合伺服器環境。它最適合的是簡單的命令列程式。
2.並行垃圾回收器(Parallel Garbage Collector)
並行垃圾回收器也叫做 throughput collector 。它是JVM的預設垃圾回收器。與序列垃圾回收器不同,它使用多執行緒進行垃圾回收。當執行垃圾回收的時候,它也會凍結所有的應用程式執行緒。
3.併發標記掃描垃圾回收器(CMS Garbage Collector)
併發標記垃圾回收使用多執行緒掃描堆記憶體,標記需要清理的例項並且清理被標記過的例項。併發標記垃圾回收器只會在下面兩種情況持有應用程式所有執行緒。
- 當標記的引用物件在tenured區域;
- 在進行垃圾回收的時候,堆記憶體的資料被併發的改變。
相比並行垃圾回收器,併發標記掃描垃圾回收器使用更多的CPU來確保程式的吞吐量。如果我們可以為了更好的程式效能分配更多的CPU,那麼併發標記上掃描垃圾回收器是更好的選擇相比並發垃圾回收器。
通過JVM引數 XX:+USeParNewGC
開啟併發標記掃描垃圾回收器。
4.G1垃圾回收器(G1 Garbage Collector)
G1垃圾回收器適用於堆記憶體很大的情況,他將堆記憶體分割成不同的區域,並且併發的對其進行垃圾回收。G1也可以在回收記憶體之後對剩餘的堆記憶體空間進行壓縮。併發掃描標記垃圾回收器在STW情況下壓縮記憶體。G1垃圾回收會優先選擇第一塊垃圾最多的區域
通過JVM引數 –XX:+UseG1GC
使用G1垃圾回收器
Java 8 的新特性
在使用G1垃圾回收器的時候,通過 JVM引數 -XX:+UseStringDeduplication
。 我們可以通過刪除重複的字串,只保留一個char[]來優化堆記憶體。這個選擇在Java 8 u 20被引入。
六、垃圾回收的JVM配置
執行的垃圾回收器型別
配置 | 描述 |
---|---|
-XX:+UseSerialGC | 序列垃圾回收器 |
-XX:+UseParallelGC | 並行垃圾回收器 |
XX:ParallelGCThreads= | 並行垃圾回收器,執行緒數 |
-XX:+UseConcMarkSweepGC | 併發標記掃描垃圾回收器 |
-XX:ParallelCMSThreads= | 併發標記掃描垃圾回收器,執行緒數 |
-XX:+UseG1GC | G1垃圾回收器 |
GC的優化配置
配置 | 描述 |
---|---|
-Xms | 初始化堆記憶體大小 |
-Xmx | 堆記憶體最大值 |
-Xmn | 新生代大小 |
-XX:PermSize | 初始化永久代大小 |
-XX:MaxPermSize | 永久代最大容量 |
參考文章
1.https://www.cnblogs.com/wjtaigwh/p/6635484.html
2.http://www.importnew.com/13827.html