OutOfMemoryError: GC overhead limit exceeded是Java虚拟机在运行过程中抛出的一种内存溢出错误,它和普通的堆内存溢出不同,核心特征是垃圾回收的效率已经低到无法满足应用的内存需求。当JVM发现垃圾回收花费的时间占总运行时间的比例过高,同时回收的内存占比极低时,就会触发这个错误,避免应用陷入无意义的GC循环。

报错触发机制
JVM判断是否需要抛出这个错误的逻辑有明确的阈值:如果最近一段时间里,垃圾回收的时间占总运行时间的比例超过98%,并且每次GC后释放的堆内存不足2%,同时这种情况连续发生多次,JVM就会抛出OutOfMemoryError: GC overhead limit exceeded。这个机制的设计目的是提前预警,防止应用长时间卡在GC过程中却没有实际进展。
常见触发场景
- 堆内存设置过小,应用需要的内存远超JVM分配的最大堆内存,导致GC频繁触发但回收效果不佳。
- 代码中存在内存泄漏,比如长生命周期的对象持有短生命周期对象的引用,导致这些对象无法被回收,堆内存逐渐被占满。
- 应用短时间内创建大量临时对象,比如循环中产生大量未复用的字符串、集合对象,导致新生代内存快速占满,频繁触发Minor GC甚至Full GC。
- 使用的集合类没有合理设置初始容量,频繁扩容产生大量中间对象,增加GC压力。
排查方法
查看GC日志
首先可以开启JVM的GC日志,观察GC的频率、耗时和回收效果。可以在JVM启动参数中添加以下配置:
// 开启GC日志,输出到指定文件,打印GC详细信息 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log // 开启GC overhead limit exceeded的详细日志 -XX:+PrintGCDetails
通过分析GC日志可以看到每次GC的耗时、回收前后的堆内存大小,判断是否符合GC overhead limit exceeded的触发条件。
使用内存分析工具
可以生成堆转储文件,使用MAT、JProfiler等工具分析内存中的对象分布,找到占用内存最多的对象类型,排查是否存在内存泄漏。生成堆转储文件的JVM参数如下:
// 当发生内存溢出时自动生成堆转储文件 -XX:+HeapDumpOnOutOfMemoryError // 指定堆转储文件的保存路径 -XX:HeapDumpPath=/path/to/heap_dump.hprof
解决思路
- 调整JVM堆内存大小,根据应用的实际内存需求合理设置
-Xms和-Xmx参数,避免堆内存过小。 - 优化代码逻辑,避免不必要的对象创建,比如复用对象、使用StringBuilder代替字符串拼接、及时释放不再使用的对象引用。
- 如果存在内存泄漏,根据内存分析结果找到泄漏点,比如未关闭的流、未清理的缓存、静态集合持有过多对象等问题,针对性修复。
- 如果暂时无法修改代码,也可以通过在JVM参数中添加
-XX:-UseGCOverheadLimit关闭这个错误预警,但这只是临时方案,最终还是需要解决根本的内存问题。
示例代码说明
下面是一段可能触发该报错的示例代码,循环中不断向集合添加对象且不释放引用:
import java.util.ArrayList;
import java.util.List;
public class GcOverheadDemo {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
// 循环创建大对象并添加到集合,对象无法被回收,最终导致GC效率过低
while (true) {
// 每次创建1MB大小的字节数组
byte[] bytes = new byte[1024 * 1024];
list.add(bytes);
}
}
}
如果运行这段代码时设置较小的堆内存,比如-Xmx100m,很快就会触发OutOfMemoryError: GC overhead limit exceeded报错。如果要修复这个问题,可以在不需要对象时及时清理集合,或者合理设置堆内存大小。
JavaOutOfMemoryErrorGC_overhead_limit_exceeded垃圾回收修改时间:2026-07-13 01:39:24