跳转至

JDK 11:JVM 与运行时更新#

版本信息

  • JDK:11(LTS,2018-09 GA)
  • 本页覆盖的关键 JVM JEP248(G1 默认)、 307(G1 并行 Full GC)、 318(Epsilon 无操作 GC)、 333(ZGC 实验)、 328(JFR 开源)、 330(单文件启动)

本页讲 「工具与运行时」 这一桶:升级到 JDK 11,JVM 本身发生了什么变化。语言特性见 var

运行时主线#

从 JDK 8 升到 11,JVM 层最该知道的三件事

  1. 默认 GC 从 Parallel 换成了 G1(其实 JDK 9 起就换了,11 只是 LTS 承接)——停顿更可控、配置更省心。
  2. 两台全新实验性 GC 登场:Epsilon(只分配不回收)与 ZGC(低延迟)——为后续 LTS 的 GC 演进铺路。
  3. JFR(Java Flight Recorder)开源单文件源码可直接 java X.java 启动——观测与上手成本双双降低。

G1 成为默认(并补上并行 Full GC)#

为什么需要#

JDK 8 在服务器级机器上默认用 Parallel GC:吞吐高,但 Full GC 是整堆、长停顿。G1 把堆切成多个区域(region),优先回收"垃圾最多"的区域,能在较大堆上把停顿控制在目标内。

怎么用#

JDK 9 起(JEP 248)G1 已是默认,什么都不用配。想显式指定或调停顿目标:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar app.jar

并行 Full GCJEP 307,JDK 10 引入、JDK 11 LTS 承接):JDK 10 之前的 G1 Full GC 是单线程兜底,此后改为多线程并行,worst-case 停顿大幅缩短。

底层原理#

graph LR
    H["堆 = 多个 region"] --> Y["年轻代 region<br/>Eden/Survivor"]
    H --> O["老年代 region"]
    H --> G["大对象 humongous region"]
    Y -->|GC 优先回收垃圾最多的 region| P["可控停顿 Pause"]
    O -.Full GC(JDK 11 起并行).- P
    style H fill:#bbdefb
    style P fill:#ffe0b2

Epsilon:无操作(no-op)GC#

为什么需要#

有时你根本不想回收——做性能基线(隔离 GC 噪音)、跑短生命周期程序、压测分配吞吐。Epsilon(JEP 318)就是一个只分配、永不回收的 GC:堆满即 OOM,不做任何回收工作。

怎么用#

JDK 11 中 Epsilon 是实验特性,需先解锁再启用(本机实测可用):

java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -jar app.jar

下面是 examples/epsilon-gc/真实可运行的演示(snippets 嵌入,零漂移):

package com.javamodern.epsilon;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 演示 Epsilon:无操作(no-op)垃圾收集器(JEP 318,JDK 11 实验)。
 *
 * <p>Epsilon 只分配内存、永不回收,用于性能测试与最后一滴延迟优化。
 * 测试 JVM 必须以 {@code -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC} 启动
 * (见 {@code pom.xml} 的 surefire {@code argLine})。
 */
public class EpsilonGcDemo {

    /** 当前所有 GC MXBean 的名字(Epsilon 下只有一个 "Epsilon Heap")。 */
    public static List<String> gcNames() {
        return ManagementFactory.getGarbageCollectorMXBeans().stream()
                .map(GarbageCollectorMXBean::getName)
                .collect(Collectors.toList());
    }

    /**
     * 触发一次显式 GC 后,所有 GC bean 累计回收次数之和。
     *
     * <p>Epsilon 把 {@code System.gc()} 当作 no-op,故恒为 {@code 0};
     * 而 G1 等会递增(实测对照见文档)。
     */
    public static long totalCollectionsAfterGc() {
        System.gc(); // (1)
        long total = 0;
        for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) {
            if (b.getCollectionCount() >= 0) {
                total += b.getCollectionCount();
            }
        }
        return total;
    }

    public static void main(String[] args) {
        System.out.println("GC beans = " + gcNames());
        System.out.println("collections after System.gc() = " + totalCollectionsAfterGc());
    }
}
  1. :material-lightbulb: System.gc() 在 Epsilon 下是 no-op——它不回收,累计回收次数恒为 0。换 G1 跑同一段代码,次数会递增。

运行 EpsilonGcDemo.main 的实测输出(JDK 11 + Epsilon):

GC beans = [Epsilon Heap]
collections after System.gc() = 0

对照:同样的代码在 G1 下 collections after System.gc()1——证明 Epsilon 真的不回收。本仓库 examples/epsilon-gc 的测试用这条差异做断言(EpsilonGcDemoTest)。

底层原理#

graph LR
    A["new Object()"] --> B["Epsilon 线性分配<br/>仅移动指针 bump-the-pointer"]
    B --> C{"堆满?"}
    C -- 否 --> B
    C -- 是 --> OOM["OutOfMemoryError<br/>不回收"]
    style A fill:#bbdefb
    style OOM fill:#ffcdd2

其他运行时更新#

特性 JEP 说明(本机实测)
ZGC(实验) 333 低延迟 GC 在 11 首次实验性引入;本机 OpenJDK 11.0.0.2 构建未含,需含 ZGC 的构建(JDK 17 起转正,见 JDK 17
JFR 开源 328 飞行记录器从商业特性转为开源:java -XX:StartFlightRecording=duration=60s,filename=app.jfr -jar app.jar,再用 jcmd <pid> JFR.dump 取文件
单文件启动 330 java Hello.java 直接编译+运行单文件,免去 javac;JDK 8 不支持
低开销堆 profiling 331 JVMTI 提供低开销的堆分配采样,供 APM 工具用
嵌套类访问 181 Nest-Based Access Control:同一嵌套内的私有访问不再靠合成桥接方法
容器感知 —(JDK 10 起) Linux 容器内 JVM 正确读取 cgroup 内存/CPU 限额(Windows 不可复现)

与 JDK 8 对比#

java -jar app.jar            # 默认 Parallel:吞吐优先,Full GC 长停顿
javac Hello.java && java Hello   # 必须先编译
java -jar app.jar            # 默认 G1:停顿可控
java -XX:+UseEpsilonGC -jar bench.jar   # 实验:完全不回收
java -XX:StartFlightRecording=duration=60s,filename=app.jfr -jar app.jar
java Hello.java             # 单文件直接跑

常见坑 / 最佳实践#

  • 升到 11 就已切到 G1:别再无脑加 -XX:+UseParallelGC,除非吞吐型负载有实测收益。
  • Epsilon 会 OOM:它不回收,堆满即崩;只用于受控的短任务或压测,生产长跑别用。
  • 实验性标志要解锁:JDK 11 的 Epsilon/ZGC 需 -XX:+UnlockExperimentalVMOptions 前缀,漏了会报 Unrecognized VM option
  • JFR 默认开销极低(<1%):可常开做生产诊断;但 .jfr 文件要用 jcmd 或 JDK Mission Control 分析。

小结#

JDK 11 的 JVM 升级围绕「让默认值更好 + 给未来铺路」:G1 默认化让多数应用直接受益,Epsilon/ZGC 实验性登场为低延迟铺路,JFR 开源与单文件启动降低观测与上手门槛。

参考#