Java常见异常:java.lang.OutOfMemoryError: GC overhead limit exceeded

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44455388/article/details/101446190

今天在使用spark任务计算千万级数据时,出现了OOM:

java.lang.OutOfMemoryError: GC overhead limit exceeded

原先提交任务的参数配置:

--master yarn-cluster --driver-memory 6g --num-executors 8 --executor-memory 18g --executor-cores 4

调大driver-memory后,问题得到了解决:

--master yarn-cluster --driver-memory 20g --num-executors 8 --executor-memory 18g --executor-cores 4

问题虽然得到了解决,但仍有些不太明白的地方:

  1. driver不做任何计算和存储,只是下发任务与yarn资源管理器和task交互,一般只需要给4-6g即可。为什么调大了driver-memory的内存,问题得到了解决?
  2. 关于JVM的OOM是如何产生的?

关于第一个问题,可以参考此文章的相关论述。
关于第二个问题,我们需要了解JVM的内部机制:
这个异常表示您的Java程序在运行的时候, 98%的时间都在执行GC回收, 但是每次回收只回收不到2%的空间!
换句话说,其实这个异常往往是抛出java.lang.OutOfMemoryError: Java heap space异常的前兆! 因为Java程序每次都GC回收只能回收一点点内存空间,而你的程序却仍然在不停的产生新的对象实例, 这无疑导致了两种可能结果:

  • 不停的进行GC
  • 直接超出的堆内存大小

这个问题还有一些细节需要我们去掌握,我们先从下面的例子来看吧

public static void main(String args[]) throws Exception {
        Map map = System.getProperties();
        Random r = new Random();
        while (true) {
            map.put(r.nextInt(), "value");
        }
}

以上代码说明: 这段代码不停的往map中加入新的key-value,导致map大小不断变大! 当到达堆内存顶点的时候,GC发生, 但是清理完毕后,JVM发现清理前后的堆内存大小改变很小,不到2%; 这时候程序继续运行,继续往map中加数据!GC又发生了!又只清理不到2%! 如此不停的循环, 最后JVM得出了一个判断! 你的Java程序在占用CPU进行运算的时间里,98%的时间都特么的在垃圾回收,而每次GC居然只能回收堆内存的2%空间, 这肯定是代码存在问题,于是抛出了这个异常. 如果这个时候,你断定不是自己的代码问题, 使用JVM参数-XX:-UseGCOverheadLimit来关闭这种检查! 然后你就会发现你的程序抛出了堆溢出异常! 为什么呢? 因为堆内存不断的被占满,最终导致最后一次加入新的int的时候, 堆内存空间直接不足了!

解决方法:

  1. JVM参数
    JVM给出一个参数避免这个错误:-XX:-UseGCOverheadLimit
    但是,这个参数并不是解决了内存不足的问题,只是将错误发生时间延后,并且替换成java.lang.OutOfMemoryError: Java heap space
  2. 堆内存
    还有一个偷懒的方法是:增大堆内存。既然堆内存少了,那就增加堆内存即可。
    但是,这个方法也不是万能的。因为程序里可能有内存泄露。这个时候即使再增大堆内存,也会有用完的时候。
    所以前两个方法都只是治标不治本而已。
  3. 终极方法
    其实还是有一个终极方法的,而且是治标治本的方法,就是找到占用内存大的地方,把代码优化了,就不会出现这个问题了。
    怎么找到需要优化的代码呢?就是通过heap dump生产jvm快照,通过分析快照找到占用内存大的对象,从而找到代码位置。
    通过设置-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump参数来生产快照,然后通过VisualVM或者MAT等工具分析快照内容进行定位。通过这个参数是将发生OOM时的堆内存所有信息写入快照文件,也就是说,如果此时堆内存中有敏感信息的话,那就可能造成信息泄漏了。

猜你喜欢

转载自blog.csdn.net/weixin_44455388/article/details/101446190
今日推荐