1. 简介

Java 11 引入了一个名为 EpsilonNo-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


原始标题:An Introduction to Epsilon GC: A No-Op Experimental Garbage Collector