1. 简介
Java 11 引入了一个名为 Epsilon 的 No-Op 垃圾收集器,它承诺提供 最低的 GC 开销。
在这篇简短的文章中,我们将探讨 Epsilon GC 是如何工作的,并介绍一些常见的使用场景。
2. 快速上手体验
我们先来动手试一试,看看 Epsilon GC 到底是怎么回事!
首先,我们需要一个能制造垃圾的应用程序:
class MemoryPolluter {
static final int MEGABYTE_IN_BYTES = 1024 * 1024;
static final int ITERATION_COUNT = 1024 * 10;
static void main(String[] args) {
System.out.println("Starting pollution");
for (int i = 0; i < ITERATION_COUNT; i++) {
byte[] array = new byte[MEGABYTE_IN_BYTES];
}
System.out.println("Terminating");
}
}
这段代码在循环中不断创建 1MB 的数组。由于循环了 10240 次,总共分配了约 10GB 内存,这通常会超出 JVM 默认的堆大小。
我们在关键节点加了日志输出,方便观察程序执行情况。
✅ 要启用 Epsilon GC,需要添加以下 JVM 参数:
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
运行程序后,会得到如下错误:
Starting pollution
Terminating due to java.lang.OutOfMemoryError: Java heap space
⚠️ 但如果使用默认的垃圾收集器运行相同的程序,它能正常执行完成:
Starting pollution
Terminating
❓问题来了:为什么使用 Epsilon 就失败了?明明这么点垃圾,随便哪个 GC 都能轻松搞定吧?
接下来我们来看看 Epsilon GC 的设计原理,就能理解为什么会这样了。
3. Epsilon GC 工作原理
Epsilon 是一个 no-op(无操作)垃圾收集器。
JEP 318 中明确指出:
“Epsilon 处理内存分配,但不实现任何实际的内存回收机制。一旦可用的 Java 堆耗尽,JVM 将关闭。”
这也就解释了为什么我们的程序会因为 OutOfMemoryError
而终止。
但这就引出一个问题:
❓为什么我们需要一个不回收垃圾的垃圾收集器?
有些场景下,我们 明确知道堆内存是足够的,所以不希望 JVM 浪费资源去执行 GC 任务。
以下是一些典型应用场景(同样来自 JEP):
- ✅ 性能测试(排除 GC 干扰)
- ✅ 内存压力测试
- ✅ 虚拟机接口测试
- ✅ 极短生命周期的任务
- ✅ 对延迟极度敏感的场景(榨干最后一点性能)
- ✅ 对吞吐量极度敏感的场景(榨干最后一点性能)
4. 结论
在这篇文章中,我们了解了 Java 11 中引入的 Epsilon GC —— 一个“不干活”的垃圾收集器。我们讨论了它的工作机制,以及它适用的典型场景。
虽然它不能用于生产环境的常规应用,但在特定测试和优化场景中,它是一个非常有价值的工具。
📌 示例代码可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-11