1. 概述
首先什么是GC?JVM 负责在对象不再被使用时释放内存,这个过程称为垃圾回收(Garbage Collection,简称 GC)。java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
错误表明内存被耗尽。
本文,我们将探讨导致 java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
错误的原因以及如何解决这个问题。
2. 错误原因
OutOfMemoryError
是 java.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 上找到。