epsilon gc 是仅分配不回收的实验性垃圾收集器,jdk 11–15 支持,需同时指定 -xx:+unlockexperimentalvmoptions 和 -xx:+useepsilongc;它用确定性 jvm 退出替代 oom,适用于剥离 gc 干扰的压力测试与内存泄漏验证。

Epsilon GC 不是用来“运行”应用的,而是用来“撞墙”的——它不回收内存,只分配,堆一满就退出。想靠它做压力测试或基准评估,必须放弃“程序稳定运行”的预设,转而把崩溃当作有效信号。
为什么 -XX:+UseEpsilonGC 启动就报错?必须加 -XX:+UnlockExperimentalVMOptions
Epsilon 是实验性功能,JDK 11 默认禁用。漏掉解锁参数会直接报错:Unrecognized VM option 'UseEpsilonGC' 或 VM option 'UseEpsilonGC' is experimental。
- 启动命令必须包含两个参数:
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC - 顺序无关,但缺一不可;
-XX:+DisableExplicitGC可省略,因为 Epsilon 本身无视System.gc() - JDK 17+ 已移除 Epsilon,仅限 JDK 11–15(部分 16 构建版),确认版本再试
用 -Xmx 设定内存上限,把 OOM 变成可预期的退出
Epsilon 不抛 OutOfMemoryError,而是直接终止 JVM 进程,退出码通常为 143(SIGTERM)或 137(SIGKILL)。这正是压力测试需要的确定性行为。
- 例如:限定最多用 512MB,启动参数写
-Xmx512m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC - 程序一旦累计分配超 512MB(含元空间、线程栈等开销),JVM 立即退出,不会尝试回收
- 配合
jstat -gc <pid></pid>无意义——Epsilon 没有 GC 统计项;可用jcmd <pid> VM.native_memory summary</pid>查实时内存占用
写测试代码时,别依赖 finalize() 或弱引用,Epsilon 不触发任何回收逻辑
所有基于“对象被回收时回调”的机制在 Epsilon 下完全失效。常见误用包括:
- 在
finalize()里打日志验证 GC 行为 → 实际永不执行,System.out.println("dying")永远不会出现 - 用
WeakReference/PhantomReference监听对象存活 → 引用永远不入队,ReferenceQueue.poll()始终返回null - 期望
System.gc()触发清理 → 它静默忽略,连警告都不打(除非开启-XX:+PrintGCDetails,才可能看到提示 “explicit GC disabled”)
真实压测中,Epsilon 的价值不在吞吐量,而在剥离 GC 噪声后的延迟归因
你测出的 99% 延迟下降了 2ms?那未必是代码优化的结果——可能是躲开了 CMS 的并发预清理暂停,或是绕过了 G1 的混合 GC 扫描阶段。Epsilon 把 GC 影响压缩到零,让延迟毛刺真正暴露底层问题。
- 对比测试建议:同一负载下,分别跑
-XX:+UseG1GC和-XX:+UseEpsilonGC,用async-profiler采集 CPU 火焰图,观察 GC 相关线程(如G1 Refine、ConcurrentMark)是否消失 - 注意:Epsilon 不降低单次分配成本,反而略高(因跳过写屏障),所以纯分配密集型场景(如大量小对象创建)可能比 G1 更慢
- 真正适合它的场景是:已知内存用量可控的短任务(如 CLI 工具、批处理脚本)、或需验证“某段逻辑是否真没泄漏”的单元测试
最容易被忽略的一点:Epsilon 不管理元空间(Metaspace),-XX:MaxMetaspaceSize 仍生效。如果测试中动态生成大量类(如反复 Unsafe.defineClass),元空间 OOM 会先于堆耗尽发生,导致退出原因误判。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











