最近一个上线运行良好的项目出现用户无法登录或者执行某个操作时,有卡顿现象。查看了日志,出现了大量的 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问题归根结底还是代码的问题,和内存无关,代码中出现了大量占用内存的对象。