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
那麼這裏再詳細科普一下,下面是每個參數對應的回收器的類型
將信將疑的我再次開啓探索之旅,這時候我使用
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 收集器,只是書中沒有更新這個細節。
最近在重讀《深入理解 Java 虛擬機》第三版,先後看了三次,每次感觸都不一樣,如果你也和我一樣對 Java 虛擬機有興趣。關注我,私信回覆“虛擬機”獲得《深入理解 Java 虛擬機》第三版電子書的免費領取方式!