🍀 怎麼查看默認的垃圾收集器是哪個?
JVM 參數:java -XX:+PrintCommandLineFlags -version
下圖紅色就是默認垃圾收集器
🍀 默認的垃圾收集器有哪些?
java 的 gc 回收的類型主要有幾種:
UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
看下面的代碼就非常清楚 其實還有一種 UseSerialOldGC
🍀 垃圾收集器
垃圾收集器就來具體實現這些 GC 算法並實現內存回收。
不同廠商、不同版本的虛擬機實現差別很大,HotSpot中包含的收集器如下圖所示:
📌 部分參數預先說明
- DefNew —> Default New Generation
- Tenured —> Old
- ParNew —> Parallel New Generation
- PSYoungGen—> Parallel Scavenge
- ParOldGen—> Parallel Old Generation
📌 Server / Client 模式分別是什麼意思
-
1、適用範圍:只需要掌握 Server 模式即可,Client 模式基本不會用
-
2、操作系統:
- 2.1、32位Window操作系統,無論硬件如何都默認使用Client的JVM模式
- 2.2、32位其它操作系統,2G內存同時有2個cpu以上用 Server 模式,低於該配置還是 Client 模式
- 2.3、64位 only server 模式
📌 新生代
⏳ 串行 GC(Serial)/ (Serial Copying)
【串行收集器:Serial 收集器】
一句話:一個單線程的收集器,在進行垃圾收集時候,必須暫停其他所有的工作線程直到它收集結束。
串行收集器是最古老,最穩定以及效率高的收集器,只使用一個線程去回收但其在進行垃圾收集過程中可能會產生較長的停頓("Stop-The-World"狀態)。雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對於限定單個 CPU 環境來說,沒有線程交互的開銷可以獲得最高的單線程垃圾收集效率,因此 Serial 垃圾收集器依然是 java 虛擬機運行在 Client 模式下默認的新生代垃圾收集器。
對應 JVM 參數是:-XX:+UseSerialGC
開啓後會使用:Serial(Young區用)+ Serial Old(Old區用)的收集器組合
表示:新生代、老年代都會使用串行回收收集器,新生代使用複製算法,老年代使用標記-整理算法
⏳ 並行 GC(ParNew)
【ParNew(並行)收集器】
一句話:使用多線程進行垃圾回收,在垃圾收集時,會 Stop-the-World 暫停其他所有的工作線程直到它收集結束。
ParNew收集器其實就是 Serial 收集器新生代的並行多線程版本,最常見的應用場景是配合老年代的 CMS GC 工作,其餘的行爲和 Serial 收集器完全一樣,ParNew 垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程。它是很多java虛擬機運行在Server模式下新生代的默認垃圾收集器。
常用對應JVM參數:-XX:+UseParNewGC 啓動 ParNew 收集器,隻影響新生代的收集,不影響老年代
開啓上述參數後,會使用:ParNew(Young區用) + Serial Old的收集器組合,新生代使用複製算法,老年代採用標記-整理算法
但是,ParNew+Tenured這樣的搭配,Java8 已經不再被推薦
Java HotSpot™ 64-Bit Server VM warning:
Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
備註:
-XX:ParallelGCThreads 限制線程數量,默認開啓和CPU數目相同的線程數
⏳ 並行回收 GC(Parallel)/(Parallel Scavenge)
Parallel Scavenge 收集器類似 ParNew 也是一個新生代垃圾收集,使用複製算法,也是一個並行的多線程的垃圾收集器,俗稱吞吐量優先收集器。 一句話:串行收集器在新生代和老年代的並行化
它重點關注的是:
可控制的吞吐量(Thoughput=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),也即比如程序運行100分鐘,垃圾收集時間1分鐘,吞吐量就是99%)。高吞吐量意味着高效利用CPU的時間,它多用於在後臺運算而不需要太多交互的任務。
自適應調節策略也是ParallelScavenge收集器與ParNew收集器的一個重要區別。(自適應調節策略:虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間(-XX:+MaxGCPauseMillis)或最大的吞吐量。)
常見 JVM 參數:-XX:+UseParallelGC 或 -XX:+UseParallelOldGC(可互相激活) 使用Parallel Scavenge收集器
開啓該參數後:新生代使用複製算法,老年代使用標記-整理算法
多說一句:-XX:ParallelGCThreads=數字N 表示啓動多少個GC線程
cpu>8 N = 5/8
cpu<8 N = 實際個數
📌 老年代
⏳ 串行 GC(Serial Old)/ (Serial MSC)
Serial Old 是 Serial 垃圾收集器老年代版本,它同樣是個單線程的收集器,使用標記-整理算法,這個收集器也主要是運行在 Client 默認的 Java 虛擬機默認的年老代垃圾收集器。
在 Server 模式下,主要有兩個用途(瞭解,版本已經到8及以後):
1、在JDK1.5之前版本中與新生代的 Parallel Scavenge 收集器搭配使用。(Parallel Scavenge + Serial Old)
2、作爲老年代版中使用 CMS 收集器的後備垃圾收集方案。
⏳ 並行 GC(Parallel Old)/(Parallel MSC)
Parallel Old 收集器是 Parallel Scavenge 的老年代版本,使用多線程的標記-整理算法,Parallel Old收集器在JDK1.6纔開始提供。
在JDK1.6之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量。在 JDK1.6 之前(Parallel Scavenge + Serial Old)
Parallel Old 正是爲了在年老代同樣提供吞吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,JDK1.8後可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。在 JDK1.8 及後(Parallel Scavenge + Parallel Old)
JVM 常用參數:
-XX:+UseParallelOldGC 使用 Parallel Old 收集器,設置該參數後,新生代 Parallel + 老年代 Parallel Old
⏳ 併發標記清除 GC(CMS)
CMS收集器(Concurrent Mark Sweep:併發標記清除)是一種以獲取最短回收停頓時間爲目標的收集器。
適合應用在互聯網站或者B/S系統的服務器上,這類應用尤其重視服務器的響應速度,希望系統停頓時間最短。
CMS非常適合堆內存大、CPU核數多的服務器端應用,也是G1出現之前大型應用的首選收集器。
Concurrent Mark Sweep 併發標記清除,併發收集低停頓,併發指的是與用戶線程一起執行
開啓該收集器的 JVM 參數:-XX:+UseConcMarkSweepGC 開啓該參數後會自動將 -XX:+UseParNewGC 打開
開啓該參數後,使用 ParNew(Young區用) + CMS(Old區用) + Serial Old 的收集器組合,Serial Old將作爲CMS出錯的後備收集器
🎯 4步過程
- 初始標記(CMS initial mark)
- 只是標記一下 GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。
- 併發標記(CMS concurrent mark)和用戶線程一起
- 進行 GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。主要標記過程,標記全部對象
- 重新標記(CMS remark)
- 爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。
由於併發標記時,用戶線程依然運行,因此在正式清理前,再做修正
- 爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。
- 併發清除(CMS concurrent sweep)和用戶線程一起
- 清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。基於標記結果,直接清理對象
由於耗時最長的併發標記和併發清除過程中,垃圾收集線程可以和用戶現在一起併發工作,
所以總體上來看 CMS 收集器的內存回收和用戶線程是一起併發地執行。
- 清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。基於標記結果,直接清理對象
🎯 優缺點
- 優
- 併發收集低停頓
- 缺
- 併發執行,對CPU資源壓力大
- 由於併發進行,CMS在收集與應用線程會同時會增加對堆內存的佔用,也就是說,CMS 必須要在老年代堆內存用盡之前完成垃圾回收,否則CMS回收失敗時,將觸發擔保機制,串行老年代收集器將會以 STW 的方式進行一次 GC,從而造成較大停頓時間
- 採用的標記清除算法會導致大量碎片
- 標記清除算法無法整理空間碎片,老年代空間會隨着應用時長被逐步耗盡,最後將不得不通過擔保機制對堆內存進行壓縮。CMS也提供了參數 -XX:+CMSFullGCsBeForeCompaction(默認0,即每次都進行內存整理)來指定多少次CMS收集之後,進行一次壓縮的 Full GC。
- 併發執行,對CPU資源壓力大
📌 垃圾收集器配置代碼總結
⏳ 底層代碼
⏳ 實際代碼
package com.brian.interview.study.jvm.gc;
/**
* Copyright (c) 2020 ZJU All Rights Reserved
* <p>
* Project: JavaSomeDemo
* Package: com.brian.interview.study.jvm.gc
* Version: 1.0
* <p>
* Created by Brian on 2020/2/15 22:46
*/
import java.util.Random;
/**
* 1、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC (DefNew+Tenured)
*
* 2、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC (ParNew+Tenured)
*
* 備註情況:Java HotSpot(TM) 64-Bit Server VM warning:
* Using the ParNew young collector with the Serial old collector is deprecated
* and will likely be removed in a future release
*
* 3、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC (PSYoungGen+ParOldGen)
*
* 4、
* 4.1、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC (PSYoungGen+ParOldGen)
* 4.2、 不加就是默認 UseParallelGC
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags (PSYoungGen+ParOldGen)
*
* 5、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC (par new generation + concurrent mark-sweep generation)
*
* 6、
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC 後面單獨講解G1
*
* 7、(理論知道即可, 實際中java8已經被優化掉了, 沒有了。)
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
*
*
* 下面是故意繁瑣配置, 主要是爲了學習, 一般生產不這麼配置:
* 下面是故意繁瑣配置, 主要是爲了學習, 一般生產不這麼配置:
* 下面是故意繁瑣配置, 主要是爲了學習, 一般生產不這麼配置:
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC -XX:+UseParallelOldGC (PSYoungGen+ParOldGen)
*
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC -XX:+UseConcMarkSweepGC (par new generation + concurrent mark-sweep generation)
*/
public class GCDemo {
public static void main(String[] args) {
System.out.println("************GCDemo hello");
try {
String str = "hello";
while (true) {
str += str + new Random().nextInt(77777777) + new Random().nextInt(88888888);
str.intern();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
🍀 如何選擇垃圾收集器
- 組合的選擇
- 單CPU或小內存,單機程序
- -XX:+UseSerialGC
- 多CPU,需要最大吞吐量,如後臺計算型應用
- -XX:+UseParallelGC 或者
- -XX:+UseParallelOldGC
- 多CPU,追求低停頓時間,需快速響應如互聯網應用
- -XX:+UseConcMarkSweepGC
- -XX:+ParNewGC
- 單CPU或小內存,單機程序
參數 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC | SerialGC | 複製 | SerialOldGC | 標記-整理 |
-XX:+UseParNewGC | ParNew | 複製 | SerialOldGC | 標記-整理 |
-XX:+UseParallelGC/-XX:+UseParallelOldGC | Parallel[Scavenge] | 複製 | Parallel Old | 標記-整理 |
-XX:+UseConcMarkSweepGC | ParNew | 複製 | CMS + Serial Old 的收集器組合(Serial Old作爲CMS出錯的後備收集器) | 標記-清除 |
-XX:+UseG1GC | G1整體上採用標記-整理算法 | 局部是通過複製算法,不會產生內存碎片。 |