现象:
由于需要将mysql表中的过期数据在凌晨定时读取出过滤后转入到MongoDB,一个转换SQL达到百行,而且有几十个,集中运行后程序反馈异常:
Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
Heap内存:1.5G,程序在Docker容器限制使用内存2G。
监控到内存GC变化:
Heap内存占用骤升至1.2G,然后不停的进行FullGC,而且间隔非常短,从下图中可以看出PermGen稳定,这也表明读取的数据由于太大是直接进入了老年代内存。
这时候CPU也彪升接近100%
请求访问时长也加长,异常反馈。
java.lang.OutOfMemoryError: GC overhead limit exceeded 这种情况发生的原因是程序基本上耗尽了所有的可用内存, GC 也清理不了。
更准确的说法应该是:执行垃圾收集的时间比例太大,有效的运算量太小。默认情况下,如果GC花费的时间超过 98%,并且GC 回收的内存少于 2%,JVM 就会抛出这个错误。
网友的解决建议:
有的人在解决 “java.lang.OutOfMemoryError: GC overhead limit exceeded” 错误时,配置了下面的启动参数:
// 不推荐
-XX:-UseGCOverheadLimit
我告诉你,这是一种完全错误的做法。因为 UseGCOverheadLimit 这样使用并不能真正地解决问题,只能推迟一点 out of memory 错误发生的时间,到最后还得进行其他处理。指定这个选项,会将原来的 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误掩盖,变成更常见的 java.lang.OutOfMemoryError: Java heap space 错误消息。
有时候触发 GC overhead limit 错误的原因, 是因为分配给JVM的堆内存不足。这种情况下只需要增加堆内存大小即可。
在大多数情况下, 增加堆内存并不能解决问题。例如程序中存在内存泄漏, 增加堆内存只能推迟产生 java.lang.OutOfMemoryError: Java heap space 错误的时间。
所以,要想从根本上解决问题,则需要排查内存分配相关的代码。简单来说,需要搞清楚一下两点:
- 哪类对象占用了最多内存?
- 这些对象是在哪部分代码中分配的?
- OutOfMemoryError overhead exceeded limit GCoutofmemoryerror exceeded overhead limit outofmemoryerror overhead exceeded limit outofmemoryerror exceeded overhead内存 outofmemoryerror overhead项目build kettle outofmemoryerror java exceeded operation exceeded mongo limit overhead outofmemoryerror outofmemoryerror内存oom outofmemoryerror quot exception thread