1. 概述

首先什么是GC?JVM 负责在对象不再被使用时释放内存,这个过程称为垃圾回收(Garbage Collection,简称 GC)。java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 错误表明内存被耗尽。

本文,我们将探讨导致 java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 错误的原因以及如何解决这个问题。

2. 错误原因

OutOfMemoryErrorjava.lang.VirtualMachineError 的子类。当 JVM 遇到与资源使用相关的问题时,会抛出这个异常。具体来说,当 JVM 在垃圾回收上花费了太多时间,而只回收了很小一部分堆空间时,就会发生这个错误。

根据 Java 官方文档,默认情况下,如果 Java 进程在垃圾回收耗时占用超过98%,并且每次运行只能回收不到 2% 的堆空间时,JVM 会抛出此错误。这意味着我们的应用程序几乎耗尽了所有的可用内存,而垃圾收集器花了太多时间试图清理内存,但反复失败。

在这种情况下,用户会体验到应用的极端缓慢。某些通常能在毫秒级完成的操作可能需要更长时间才能完成,因为 CPU 全部用于垃圾回收,无法执行其他任务。

3. 错误示范

让我们来看一个引发 java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 的代码片段。

例如,我们可以通过在一个无限循环中添加值来实现:

public static void addRandomDataToList() {
    List<String> dataList = new LinkedList<>();
    while (true) {
        dataList.add(String.valueOf(RANDOM.nextInt()));
    }
}

当调用这个方法时,如果使用 JVM 参数 -Xmx100m -XX:+UseParallelGC(Java 堆大小设置为 100 MB,且垃圾回收算法为 ParallelGC),我们会得到 java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 错误。有关不同垃圾回收算法的更多信息,请参阅 Oracle 的 Java 垃圾回收基础教程

通过在项目根目录下运行以下命令(https://github.com/eugenp/tutorials/tree/master/core-java-modules):

mvn exec:exec

很快就能看到这个错误。需要注意的是,在某些情况下,我们可能会先遇到堆空间不足的错误,然后再遇到 GC Overhead Limit Exceeded 错误。

4. 解决办法

理想的解决方案是通过检查代码以找出应用程序中的内存泄漏问题。

需要回答以下问题:

  • 应用程序中占用大量堆空间的对象是什么?
  • 这些对象是在源代码的哪个部分分配的?

我们还可以使用自动图形工具,如 JConsole,它有助于检测代码中的性能问题,包括 java.lang.OutOfMemoryError

最后的手段是通过修改 JVM 启动配置来增加堆大小。

例如,这将为 Java 应用提供 1 GB 的堆空间:

java -Xmx1024m com.xyz.TheClassName

然而,如果实际应用代码中有内存泄漏,这并不能解决问题,只会推迟错误的发生。因此,更明智的做法是彻底重新评估应用程序的内存使用情况。

5. 总结

在这篇文章中,我们研究了 java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 的原因及其背后的原因。

如往常一样,本文相关的源代码可在 GitHub 上找到。


« 上一篇: "Sneaky Throws" 用法
» 下一篇: Java Weekly, 第193期