徹夜研究了2天,終於知道 JDK8 默認的是哪款GC 收集器了

JDK 8 到底默認用的是哪款 GC 收集器?

爲啥是 JDK8?不是 9 也不是 10?因爲 JDK8 還是市場佔有率最高的,所以針對這個版本我做了深入的探索。

《深入理解 Java 虛擬機》第三版第 128 頁中提到 JDK 9 之前,Server 默認使用 Parallel Scavenge + Serial Old(PS MarkSweep),那麼真的是這樣的嗎? 我帶着這個疑問做了如下驗證

  1. 直接使用命令查看一下當前 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

那麼這裏再詳細科普一下,下面是每個參數對應的回收器的類型

徹夜研究了2天,終於知道 JDK8 默認的是哪款GC 收集器了

 

將信將疑的我再次開啓探索之旅,這時候我使用 
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?這時候我有兩個猜想

  1. PS MarkSweep 只是回收器的別名,他可以指代 Serial Old 和 Parallel Old,畢竟他們的實現基本一樣。
  2. -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 所以終於有了答案。

  1. PS MarkSweep 只是回收器的別名,他可以指代 Serial Old 和 Parallel Old。
  2. -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 收集器,只是書中沒有更新這個細節。

最近在重讀《深入理解 Java 虛擬機》第三版,先後看了三次,每次感觸都不一樣,如果你也和我一樣對 Java 虛擬機有興趣。關注我,私信回覆“虛擬機”獲得《深入理解 Java 虛擬機》第三版電子書的免費領取方式!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章