JDK 8 到底默認用的是哪款 GC 收集器?
爲啥是 JDK8?不是 9 也不是 10?因爲 JDK8 還是市場佔有率最高的,所以針對這個版本我做了深入的探索。
《深入理解 Java 虛擬機》第三版第 128 頁中提到 JDK 9 之前,Server 默認使用 Parallel Scavenge + Serial Old(PS MarkSweep),那麼真的是這樣的嗎? 我帶着這個疑問做了如下驗證
-
直接使用命令查看一下當前 JVM 默認參數
java -XX:+PrintCommandLineFlags -version
複製代碼
輸入內容如下
-XX:InitialHeapSize=268435456
-XX:MaxHeapSize=4294967296
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseParallelGC
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
複製代碼
第 6 行我們可以看到使用的是 -XX:+UseParallelGC
按照書中或者是網上的文章發現,設置這個參數以後默認就是如下兩個組合,新生代用 Parallel Scavenge
老年代用 Serial Old
Parallel Scavenge + Serial Old
複製代碼
那麼這裏再詳細科普一下,下面是每個參數對應的回收器的類型
參數 | 回收器 |
---|---|
-XX:-UseSerialGC | Serial + Serial Old |
-XX:-UseParNewGC | ParNew + Serial Old |
-XX:-UseParallelGC | Parallel Scavenge + Serial Old |
-XX:-UseParallelOldGC | Parallel Scavenge + Parallel Old |
-XX:-UseConcMarkSweepGC | CMS + ParNew |
-XX:-UseG1GC | G1 |
將信將疑的我再次開啓探索之旅,這時候我使用 ManagementFactory.getGarbageCollectorMXBeans()
把具體的回收器打印出來看下不就可以了嗎?詳細代碼如下
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
public class GcCollectorPrinter {
public static void main(String[] args) {
List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean bean : beans) {
System.out.println(bean.getName());
}
}
}
複製代碼
直接運行輸出內容如下
PS Scavenge
PS MarkSweep
複製代碼
這意思是 PS MarkSweep
是 Serial Old
的意思對嗎?那麼 -XX:+UseParallelOldGC
打印出來的結果又是什麼呢?我配置好參數再次運行如下兩個命令
javac GcCollectorPrinter.java
java -XX:+UseParallelOldGC GcCollectorPrinter
複製代碼
如下是打印結果
PS Scavenge
PS MarkSweep
複製代碼
等等,我更加疑惑了? -XX:+UseParallelOldGC
和 -XX:+UseParallelGC
的輸出結果都是 PS MarkSweep
,那麼他究竟是 Serial Old
還是 Parallel Old
?
這時候我有兩個猜想
-
PS MarkSweep
只是回收器的別名,他可以指代Serial Old
和Parallel Old
,畢竟他們的實現基本一樣。 -
-XX:+UseParallelGC
和-XX:+UseParallelOldGC
結果一樣,都是用的Parallel Old
好的那麼接下來開啓 GC 之旅,這個"別名"一樣沒辦法了,我直接打印一下 GC 的日誌,看下日誌裏面顯示什麼,-XX:+PrintGCDetails
這個參數就上場了,他可以輸出 GC 詳細的分區分析,我們再次運行剛纔的兩個例子如下
java -XX:+UseParallelOldGC -XX:+PrintGCDetails GcCollectorPrinter
java -XX:+PrintGCDetails GcCollectorPrinter
複製代碼
結果驚人的一致
PS Scavenge
PS MarkSweep
Heap
PSYoungGen total 76288K, used 3932K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
eden space 65536K, 6% used [0x000000076ab00000,0x000000076aed7240,0x000000076eb00000)
from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
to space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
ParOldGen total 175104K, used 0K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
object space 175104K, 0% used [0x00000006c0000000,0x00000006c0000000,0x00000006cab00000)
Metaspace used 2729K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 297K, capacity 386K, committed 512K, reserved 1048576K
複製代碼
複製代碼
可以看到 老年代都是用的 ParOldGen
那麼一點可以斷定了,-XX:+UseParallelGC
和 -XX:+UseParallelOldGC
結果一樣,都是用的 Parallel Old
。
那麼我們繼續驗證第二個疑問,PS MarkSweep
只是回收器的別名,他可以指代 Serial Old
和 Parallel Old
,可以直接使用如下命令驗證,我用的不是+
而是-
,這樣就一定強制去掉了 Parallel Old
收集器,我們看下效果
java -XX:-UseParallelOldGC -XX:+PrintGCDetails GcCollectorPrinter
複製代碼
PS Scavenge
PS MarkSweep
Heap
PSYoungGen total 76288K, used 3932K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
eden space 65536K, 6% used [0x000000076ab00000,0x000000076aed7240,0x000000076eb00000)
from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
to space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
PSOldGen total 175104K, used 0K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
object space 175104K, 0% used [0x00000006c0000000,0x00000006c0000000,0x00000006cab00000)
Metaspace used 2728K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 297K, capacity 386K, committed 512K, reserved 1048576K
複製代碼
唯一的變化就是 ParOldGen
換成了 PSOldGen
,經過查詢我們可以確定 PSOldGen
就是 Serial Old
所以終於有了答案。
-
PS MarkSweep
只是回收器的別名,他可以指代Serial Old
和Parallel Old
。 -
-XX:+UseParallelGC
和-XX:+UseParallelOldGC
結果一樣,都是用的Parallel Old
那書上說的還能有假?保險起見還是去找一些資料吧
在 JDK 8 的官網找到了一些蛛絲馬跡
鏈接:https://urlify.cn/67NnEz
Parallel compaction is enabled by default if the option -XX:+UseParallelGC has been specified. The option to turn it off is -XX:-UseParallelOldGC.
複製代碼
大致意思就是說-XX:+UseParallelGC
就會開始 Parallel
收集器除非手動關閉,那麼可是書上爲什麼說是 Serial
呢?
終於我在 JDK 源碼 commit 記錄裏面找到了答案,在 JDK 7U4 之前確實 UserParallelGC
用的就是 Serial
,在這個版本之後 Parallel
已經很成熟了,所以直接替換了舊的收集器,所以 JDK 7u4 以後的 7 和 JDK 8 老年代默認使用的都是 Parallel
收集器,只是書中沒有更新這個細節。