JDK 11:JVM 与运行时更新#
版本信息
本页讲 「工具与运行时」 这一桶:升级到 JDK 11,JVM 本身发生了什么变化。语言特性见 var。
运行时主线#
从 JDK 8 升到 11,JVM 层最该知道的三件事:
- 默认 GC 从 Parallel 换成了 G1(其实 JDK 9 起就换了,11 只是 LTS 承接)——停顿更可控、配置更省心。
- 两台全新实验性 GC 登场:Epsilon(只分配不回收)与 ZGC(低延迟)——为后续 LTS 的 GC 演进铺路。
- 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 GC(JEP 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());
}
}
- :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 开源与单文件启动降低观测与上手门槛。