Epsilon GC 是用于精确暴露变量存活时长和内存分配行为的无操作收集器,不回收、不移动对象,所有对象存活至JVM结束;需配置-XX:+UnlockExperimentalVMOptions、-XX:+UseEpsilonGC和-Xmx;适用于测量对象创建数量、分配耗时及堆使用量,崩溃即有效数据点。

Epsilon GC 不是用来延长变量寿命的,而是用来精确暴露变量实际存活时长和内存分配行为——它不回收、不移动、不清理,所有对象一旦分配就“活到 JVM 结束”,因此特别适合做变量生命周期与内存压力的基准对照测试。
明确测试目标:不是测“变量何时死”,而是测“变量是否真被创建”
Epsilon 下 finalize()、WeakReference、PhantomReference 全部失效,对象不会被回收,也不会触发任何清理逻辑。所以不能用它验证“GC 时机”,而应聚焦于:
- 确认某段代码是否真的生成了预期数量/大小的对象(比如一个算法优化后是否减少了临时字符串)
- 测量在零 GC 干扰下,对象从分配到 JVM 崩溃前的“自然驻留时间”
- 比对不同实现中相同逻辑的堆分配总量(例如 Stream.collect() vs for-loop + ArrayList)
配置必须项:两个开关 + 内存上限
缺一不可,否则启动失败或行为不符预期:
- -XX:+UnlockExperimentalVMOptions —— 解锁实验特性(JDK 11–15 专属)
- -XX:+UseEpsilonGC —— 启用 Epsilon 收集器
- -Xmx256m —— 设定硬性堆上限(推荐从 128m/256m 起步,避免过早退出)
示例完整命令:
java -Xmx256m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -jar your-benchmark.jar
编写可验证的基准代码要点
避免依赖 GC 行为,改用直接观测和退出信号:
- 用 AtomicLong 或 Unsafe.objectFieldOffset 记录分配计数,而非等待弱引用入队
- 在关键路径插入 System.nanoTime() 打点,对比不同版本间对象创建耗时差异
- 主动调用 Runtime.getRuntime().totalMemory() - freeMemory() 获取实时已分配量,判断是否逼近 -Xmx 阈值
- JVM 退出即视为“内存耗尽信号”,记录退出码(137/143)和运行时长,作为该配置下的最大存活窗口
结果解读:把崩溃当作数据点
Epsilon 的退出不是失败,而是有效测量终点:
- 同一代码在 -Xmx512m 下运行 8.2 秒后退出 → 表明该逻辑在 512MB 限制内最多支撑 8.2 秒的持续分配
- 优化后同样配置下运行 12.6 秒才退出 → 说明单位时间内对象生成率下降约 35%,验证了低垃圾设计效果
- 配合 jcmd
VM.native_memory summary 查看退出前各内存区用量,识别元空间或线程栈是否成为瓶颈
不复杂但容易忽略:Epsilon 测试的价值不在运行多久,而在让每一次内存分配都“可见、可计、不可绕”。











