最近一个上线运行良好的项目出现用户无法登录或者执行某个操作时, 有卡顿现象. 查看了日志, 出现了大量的 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误.
oracle 官方给出了这个错误产生的原因和解决方法:
- Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
- Cause: The detail message "GC overhead limit exceeded" indicates that the garbage collector is running all the time and Java program is making very slow progress. After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering Less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown. This exception is typically thrown because the amount of live data barely fits into the Java heap having little free space for new allocations.
- Action: Increase the heap size. The java.lang.OutOfMemoryError exception for GC Overhead limit exceeded can be turned off with the command line flag -XX:-UseGCOverheadLimit.
原因:
大概意思就是说, JVM 花费了 98% 的时间进行垃圾回收, 而只得到 2% 可用的内存, 频繁的进行内存回收 (最起码已经进行了 5 次连续的垃圾回收),JVM 就会曝出 ava.lang.OutOfMemoryError: GC overhead limit exceeded 错误.
java 运行环境包含了一个内置的 Garbage Collection (GC) 垃圾回收进程, 用于对不在使用的内存区域进行回收, 释放被占用的内存, jvm 会根据程序的运行情况, 执行 GC 垃圾回收操作. java 语言, 程序员只需关注内存的分配, 无需关注内存的回收.
而其他大多数的编程语言, 却需要程序员手工编写分配和释放内存的代码.
这种机制也会有一些问题, 就是被占用的内存, 经过多次长时间的 GC 操作都无法回收, 导致可用内存越来越少, 俗称内存泄露, JVM 就会报 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误.
这个是 jdk1.6 新增的错误类型.
如果没有这个异常, 会出现什么情况呢? 经过垃圾回收释放的 2% 可用内存空间会快速的被填满, 迫使 GC 再次执行, 出现频繁的执行 GC 操作, 服务器会因为频繁的执行 GC 垃圾回收操作而达到 100% 的时使用率, 服务器运行变慢, 应用系统会出现卡死现象, 平常只需几毫秒就可以执行的操作, 现在需要更长时间, 甚至是好几分钟才可以完成.
解决方法:
1, 增加 heap 堆内存.
2, 增加对内存后错误依旧, 获取 heap 内存快照, 使用 Eclipse MAT 工具, 找出内存泄露发生的原因并进行修复.
3, 优化代码以使用更少的内存或重用对象, 而不是创建新的对象, 从而减少垃圾收集器运行的次数. 如果代码中创建了许多临时对象 (例如在循环中), 应该尝试重用它们.
4, 升级 JDK 到 1.8, 最起码也是 1.7, 并使用 G1GC 垃圾回收算法.
5, 除了使用命令 - xms1g -xmx2g 设置堆内存之外, 尝试在启动脚本中加入配置:
- -XX:+UseG1GC -XX:G1HeapRegionSize=n -XX:MaxGCPauseMillis=m
- -XX:ParallelGCThreads=n -XX:ConcGCThreads=n
还有一个非常不建议使用的解决方法:
在启动脚本中添加 - XX:-UseGCOverheadLimit 命令. 这个方法只会把 "java.lang.OutOfMemoryError: GC overhead limit exceeded" 变成更常见的 java.lang.OutOfMemoryError: Java heap space 错误.
我是如何解决这个问题的呢?
首先我的项目是在 jdk1.8,64 位操作系统上运行, 服务器物理内存 64G, 内存足够, 当时分配的 4G 的内存, 并且运行了稳定运行了一年, 没有出现过内存溢出的问题.
所以我判断是内存泄漏, 内存泄露很隐秘, 基本是代码的原因, 有大量的对象占用内存, 又不能被 GC 回收, 久而久之就出现内存不足, 无法给新建的对象分配空间, 曝出 GC overhead limit exceeded.
经过分析内存, 找出原因所在, 有段代码使用 while 循环, 不停的 new 对象, 占用了大量的内存, 修改代码之后, 问题即解决.
GC overhead limit exceeded 问题归根结底还是代码的问题, 和内存无关, 代码中出现了大量占用内存的对象.
来源: https://www.cnblogs.com/airnew/p/11756450.html