【Java】JVM - 各版本默认垃圾收集器

14,757 阅读11分钟

一起了解一下 Java 各版本默认使用的垃圾收集器吧。

前言

操作系统信息

版本     Windows 11 家庭中文版
版本     21H2
安装日期   ‎2021/‎7/‎1
操作系统版本 22000.160
序列号    PF2A6MPG
体验     Windows 功能体验包 1000.22000.160.0

JDK 介绍

JDK 有很多版本,除了 Oracle 官方的之外,还有各种 OpenJDK,比如 adopt-openjdk、Amazon Corretto JDK、Azul Zulu CommunityTM JDK、Eclipse Temurin JDK 等等,甚至连微软都加入了 JDK 的开发😏

不同 JDK 用的 JVM 不同,因此默认所使用的垃圾收集器也不同。所以,同一版本 JDK 出现使用不同默认垃圾收集器的情况不必惊讶,更不必争论。没有调查就没有发言权,但即使调查了,也仅仅只有发言权,不一定是正确答案。费劲心血所收获的,也可能只是魔方的一面,而非全貌。

结果

结果在最后章节,想看结果的朋友可以直接按目录跳转。

查询默认垃圾收集器

通过 Java 命令

使用 -XX:+PrintCommandLineFlags 可以查询 Java 启动时定义好的变量,用它可以看到使用的 GC。

PS C:\Program Files\Java> java -XX:+PrintCommandLineFlags -version
-XX:ConcGCThreads=3 -XX:G1ConcRefinementThreads=13 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=257798976 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4124783616 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
java version "16.0.2" 2021-07-20
Java(TM) SE Runtime Environment (build 16.0.2+7-67)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.2+7-67, mixed mode, sharing)

通过代码

代码获取的是实际的垃圾收集器,只要在运行时不添加任何 JVM GC 参数,就会使用默认的 GC。使用代码:

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;

public class CheckDefaultGC {
    public static void main(String[] args) {
        System.out.println("Get JDK Default GC for jdk"
                + System.getProperty("java.version") + " - "
                + System.getProperty("java.vm.name") + ":");
        for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
            System.out.println(gcBean.getName());
        }
    }
}

由于 Java 向下兼容的特性,旧版本的 Java 程序可以在新版 JDK 中运行,因此使用 JDK5 编译器将程序编译为 class 文件再编译成 jar 包,供所有 JDK 运行。

不要相信 IDE 运行的结果,有些 IDE 会默认添加 JVM 参数,虽然我们可以修改或者去掉,但也是麻烦事。使用 java 命令最简单。

  • MANIFEST.MF

    Manifest-Version: 1.0
    Created-By: 1.5.0_22 (Sun Microsystems Inc.)
    Main-Class: com.test.CheckDefaultGC
    
    
  • 打包

    PS C:\Workspace\Idea\java-test\src> & 'C:\Program Files\Java\jdk1.5.0_22\bin\javac.exe' .\com\test\CheckDefaultGC.java -version
    javac 1.5.0_22
     
    PS C:\Workspace\Idea\java-test\src> & 'C:\Program Files\Java\jdk1.5.0_22\bin\jar.exe' -cfmv check-default-gc.jar .\com\test\CheckDefaultGCManifest.MF .\com\test\CheckDefaultGC.class
    标明清单(manifest)
    增加:com/test/CheckDefaultGC.class(读入= 1138) (写出= 655)(压缩了 42%)
    
  • 测试

    PS C:\Program Files\Java> java -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
    Get JDK Default GC for jdk16.0.2 - Java HotSpot(TM) 64-Bit Server VM:
    G1 Young Generation
    G1 Old Generation
    

查询默认垃圾收集器

JDK5

JDK5 是 Java 的重大里程碑版本,此前所有 JDK 都称为 JDK 1.x,自第五版本起,改为 JDKX 方式命名。

PS C:\Program Files\Java> .\jdk1.5.0_22\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk1.5.0_22 - Java HotSpot(TM) 64-Bit Server VM:
PS Scavenge
PS MarkSweep
PS C:\Program Files\Java> .\jdk1.5.0_22\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:MaxHeapSize=1073741824 
-XX:+UseParallelGC
-XX:+PrintCommandLineFlags 
java version "1.5.0_22"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_22-b03)
Java HotSpot(TM) 64-Bit Server VM (build 1.5.0_22-b03, mixed mode)
  • MaxHeapSize:最大堆大小,即 Xmx,1073741824 = 1G,可以直接指定以 g/m/k 为单位。

  • +UseParallelGC:使用 Parallel 垃圾收集器,即 PS Scavenge 和 PS MarkSweep。

注意这里的 + 号,有 +/- 号表示启用/关闭,没有的表示是配置属性。

JDK6

PS C:\Program Files\Java> .\jdk1.6.0_45\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:ParallelGCThreads=13 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
-XX:+PrintCommandLineFlags 
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
  • InitialHeapSize:初始堆大小,即 Xms,257798976 = 245.8M,可以直接指定以 g/m/k 为单位。

  • ParallelGCThreads:并行 GC 线程数量,只要是多线程 GC 就可以设置。

    默认情况下,当 CPU 数量小于8, ParallelGCThreads 的值等于 CPU 数量,当 CPU 数量大于 8 时,根据公式计算得出:ParallelGCThreads = 8+((N-8)×5/8) = 5×N/8+3,这里的 CPU 和 N 指逻辑 CPU 的数量,测试电脑为 16 核心数,因此计算结果为 13 符合。

  • +UseCompressedOops :启用压缩普通对象指针,减少指针内存使用量。

    关于压缩指针个人觉得有一篇文章写的挺好的:JVM之压缩指针(CompressedOops) (juejin.cn)

  • -UseLargePagesIndividualAllocation :关闭???

PS C:\Program Files\Java> .\jdk1.6.0_45\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk1.6.0_45 - Java HotSpot(TM) 64-Bit Server VM:
PS Scavenge
PS MarkSweep

JDK7

PS C:\Program Files\Java> .\jdk1.7.0_80\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
-XX:+PrintCommandLineFlags 
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
PS C:\Program Files\Java> .\jdk1.7.0_80\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk1.7.0_80 - Java HotSpot(TM) 64-Bit Server VM:
PS Scavenge
PS MarkSweep

JDK8

PS C:\Program Files\Java> .\jdk1.8.0_211\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
-XX:+PrintCommandLineFlags
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
  • +UseCompressedClassPointers:启用压缩类型指针。

    每个对象的对象头中都有一个指向所属类型的指针,在 64 位操作系统上默认是 8 个字节的,但开启压缩类型指针之后,可以压缩成 4 个字节,原理和 UseCompressedOops 一样。同时,开启压缩类型指针,必须先开启压缩对象指针。

PS C:\Program Files\Java> .\jdk1.8.0_211\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk1.8.0_211 - Java HotSpot(TM) 64-Bit Server VM:
PS Scavenge
PS MarkSweep

JDK9

PS C:\Program Files\Java> .\jdk-9.0.4\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC
-XX:G1ConcRefinementThreads=13 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
  • ReservedCodeCacheSize:设置代码缓存最大值,251658240 = 240M。

    代码缓存是一块独立的内存空间,常被人所忽视,用来存储 JIT 编译的本地代码,一旦代码缓存满了,就会停止 JIT 编译,此时 JVM 完全进入解释型执行。除了之前存储在代码缓存中的代码之外,其他代码不会再执行 JIT 优化,相当于解释型语言。速度会下降一个量级,因此代码缓存大小非常重要。

    同时,Java 所使用的本地方法代码(JNI)也会存在代码缓存中。如果该缓存区满了,也会影响本地方法的执行效率。

    与之对应的是 InitialCodeCacheSize 为起始代码缓存区大小。

  • +SegmentedCodeCache:开启代码缓存分段初始化。

    代码缓存区分为三块:NonNMethodCodeProfiledCodeNonProfiledCode。如果未开启代码缓存分段初始化,这三个区域就会初始化成一个整体,如果开启了,就会初始化成分开的三个区域。

  • +UseG1GC:启用 GC 垃圾收集器。

  • G1ConcRefinementThreads:G1 缓冲区线程数。

PS C:\Program Files\Java> .\jdk-9.0.4\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk9.0.4 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK10

PS C:\Program Files\Java> .\jdk-10.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=13 
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "10.0.2" 2018-07-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)
PS C:\Program Files\Java> .\jdk-10.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk10.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK11

PS C:\Program Files\Java> .\jdk-11.0.11\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "11.0.11" 2021-04-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.11+9-LTS-194)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.11+9-LTS-194, mixed mode)
  • GCDrainStackTargetSize:并发标记子阶段处理时为了保证处理的性能,一次标记的最多对象个数,默认 64。
PS C:\Program Files\Java> .\jdk-11.0.11\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk11.0.11 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK12

PS C:\Program Files\Java> .\jdk-12.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "12.0.2" 2019-07-16
Java(TM) SE Runtime Environment (build 12.0.2+10)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)
PS C:\Program Files\Java> .\jdk-12.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk12.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK13

PS C:\Program Files\Java> .\jdk-13.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976 
-XX:MaxHeapSize=4124783616 
-XX:MinHeapSize=6815736 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "13.0.2" 2020-01-14
Java(TM) SE Runtime Environment (build 13.0.2+8)
Java HotSpot(TM) 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)
  • MinHeapSize:堆区占用的最小内存,6815736 = 6.5M
PS C:\Program Files\Java> .\jdk-13.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk13.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK14

PS C:\Program Files\Java> .\jdk-14.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976
-XX:MaxHeapSize=4124783616 
-XX:MinHeapSize=6815736 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
-XX:+PrintCommandLineFlags 
java version "14.0.2" 2020-07-14
Java(TM) SE Runtime Environment (build 14.0.2+12-46)
Java HotSpot(TM) 64-Bit Server VM (build 14.0.2+12-46, mixed mode, sharing)
PS C:\Program Files\Java> .\jdk-14.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk14.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK15

PS C:\Program Files\Java> .\jdk-15.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:ConcGCThreads=3 
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976 
-XX:MarkStackSize=4194304 
-XX:MaxHeapSize=4124783616 
-XX:MinHeapSize=6815736 
-XX:+PrintCommandLineFlags 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
java version "15.0.2" 2021-01-19
Java(TM) SE Runtime Environment (build 15.0.2+7-27)
Java HotSpot(TM) 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing)
PS C:\Program Files\Java> .\jdk-15.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk15.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

JDK16

PS C:\Program Files\Java> .\jdk-16.0.2\bin\java.exe -XX:+PrintCommandLineFlags -version
-XX:ConcGCThreads=3 
-XX:G1ConcRefinementThreads=13 
-XX:GCDrainStackTargetSize=64 
-XX:InitialHeapSize=257798976 
-XX:MarkStackSize=4194304 
-XX:MaxHeapSize=4124783616 
-XX:MinHeapSize=6815736 
-XX:+PrintCommandLineFlags 
-XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation
java version "16.0.2" 2021-07-20
Java(TM) SE Runtime Environment (build 16.0.2+7-67)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.2+7-67, mixed mode, sharing)
PS C:\Program Files\Java> .\jdk-16.0.2\bin\java.exe -jar C:\Workspace\Idea\java-test\src\check-default-gc.jar
Get JDK Default GC for jdk16.0.2 - Java HotSpot(TM) 64-Bit Server VM:
G1 Young Generation
G1 Old Generation

记录

可以看到 Oracle 官方在综合考虑之下,JDK8 及以前使用的是 PS Scavenge 和 PS MarkSweep,JDK9 及之后使用的是 G1 收集器

G1 自然不用说,后面推出了 ZGC 可能是由于稳定性考虑没有马上更换,接下来 JDK17 是下一个 LTS,或许会有所更改。

PS Scavenge 和 PS MarkSweep 的选择应该是看重吞吐量高和多线程这两点。由于 CMS 使用标记清除算法,如果用作默认确实不怎么合适。

JDK VersionYoung GCOld GCLTS
JDK5_22PS ScavengePS MarkSweepx
JDK6PS ScavengePS MarkSweepx
JDK7PS ScavengePS MarkSweepx
JDK8PS ScavengePS MarkSweepLTS
JDK9G1 Young GenerationG1 Old Generationx
JDK10G1 Young GenerationG1 Old Generationx
JDK11G1 Young GenerationG1 Old GenerationLTS
JDK12G1 Young GenerationG1 Old Generationx
JDK13G1 Young GenerationG1 Old Generationx
JDK14G1 Young GenerationG1 Old Generationx
JDK15G1 Young GenerationG1 Old Generationx
JDK16G1 Young GenerationG1 Old Generationx
JDK17G1 Young GenerationG1 Old Generationx
JDK18G1 Young GenerationG1 Old Generationx
JDK19G1 Young GenerationG1 Old Generationx

有时候花了很多时间,得出来只是一个非常简单的结论。但还是那句话,没有调查就没有发言权。筚路蓝缕,朝着目标出发,结果重要,但过程也很重要。