臭名昭著,怙恶不悛的OOM,到底是什么?

分类:Java
很多Java程序员都经常会碰到OOM,而这类问题往往比较难以解决,找到root cause并不是件容易的事情,我们今天就来看看OOM到底是什么?有哪些常见的OOM,初步的解决方法有哪些?
OOM是Out Of Memory的缩写,翻译过来就是内存溢出,在程序运行过程中,如果出现了OOM,就会抛出OutOfMemoryError异常。在JVM运行时数据区中,只有程序计数器这一个区域不会出现OOM异常:

臭名昭著,怙恶不悛的OOM,到底是什么?

常见的OOM有如下这些:

臭名昭著,怙恶不悛的OOM,到底是什么?

一、Java heap space可能最常见的OOM异常,表示Java堆内存溢出,示例
package com.sample.core.oom;
// 堆内存溢出
public class HeapOOM {
    public static void main(String[] args) {
        String[] str = new String[10000 * 10000];
    }
}
然后指定VM选项,把最大堆内存设置为10M:

臭名昭著,怙恶不悛的OOM,到底是什么?

运行代码,就会出现OOM的异常:

臭名昭著,怙恶不悛的OOM,到底是什么?

可通过增加堆内存、分析代码找出分配过多对象等方式来解决。比如上面的代码,如果运行的VM选项Xmx设置为1000M,那么程序运行的时候就不会报错
二、GC Overhead limit exceeded:表示垃圾回收一直在运行从而导致程序本身运行缓慢。在垃圾回收后,如果Java进行花费89%的时间用于GC且恢复不到2%的内存,那么就会抛出这种类型的OOM。示例:
package com.sample.core.oom;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
// 设置VM参数为-Xmx10M,可以很快看到效果
public class OverheadExceedOOM {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        Random random = new Random();
        while(true){
            map.put(random.nextInt(), "互联网全栈架构");
        }
    }
}
对于这种类型的OOM,可以通过增加堆内存大小来解决,也可以添加这个VM参数来禁用这个检查:-XX:-UseGCOverheadLimit,当然,这个不会真正解决问题,只是把问题延后,最终还是出现Java heap space的OOM。当然,彻底的解决办法还是分析代码,看看到底是哪里导致了这样的问题。
三、Requested array size exceeds VM limit:顾名思义,这个错误表示数组大小超过了VM的限制,像这种OOM相对比较少见,一般情况下,也很少会声明一个数组,用于存放上亿个元素。
四、Metaspace:Java类的元数据(Java类在虚拟机中的内部表示)在堆外内存进行分配,如果存入Java类元数据的元空间用尽了,那么就会抛出这个错误,可以用参数MaxMetaSpaceSize来指定元空间的最大值。示例:
package com.sample.core.oom;

public class MetaspaceOOM {
    static javassist.ClassPool cp
            = javassist.ClassPool.getDefault();

    public static void main(String args[]) throws Exception {
        // 循环加载类
        for (int i = 0; i < 100000; i++) {
            Class c = cp.makeClass("com.sample.core.oom.MetaspaceOOM" + i)
                    .toClass();
        }
    }
}

Javassist是一个开源的分析、编辑和创建Java字节码的类库。使用它需要在pom.xml中引入以下的依赖:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>

指定VM的参数-XX:MaxMetaspaceSize=10M,运行代码,可以看到以下的OOM:

臭名昭著,怙恶不悛的OOM,到底是什么?

五、unable to create new native thread:当前JVM申请创建一个新的线程,但底层操作系统无法分配新的OS线程时,会抛出这样的异常。示例:

package com.sample.core.oom;

public class NewThreadOOM {
    public static void main(String[] args) {
        while(true){
            new Thread(() -> {
                System.out.println(Math.random() +" ");
                try {
                    Thread.sleep(10000000);
                } catch(InterruptedException e) { }
            }).start();
        }
    }
}

由于操作系统的差异,以上代码在Linux平台上进行测试,运行一段时间后,会出现以下的异常:

臭名昭著,怙恶不悛的OOM,到底是什么?

六、request size bytes for reason. Out of swap space:当本地内存不够用时会抛出此异常,可通过日志文件排查问题的根本原因。通常,这个异常跟底层的操作系统有关系,比如没有配置足够的交换空间,或者别的进程消耗了所有的内存资源。

七、Compressed class space:在64位平台上,可以使用32位的偏移指针来访问数据,它通过参数UseCompressedClassPointers来进行控制(参数默认是开启的),如果它所需的空间超过了参数CompressedClassSpaceSize指定的值,就会抛出Compressed class space的OOM异常。

八、reason stack_trace_with_native_method:在JNI(Java Native Interface)或者本地方法遇到了资源分配失败时,会抛出此异常。

以上就是关于OOM的基本介绍,以及OOM的常见类型,对于最常遇到的情况,文章给出了样例代码进行演示,后续文章会继续介绍OOM的一些排查手段等内容,敬请期待。

都看到这里了,请帮忙一键三连啊,也就是点击文末的在看、点赞、分享,这样会让我的文章让更多人看到,也会大大地激励我进行更多的输出,谢谢!

鸣谢:
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html
https://www.geeksforgeeks.org/understanding-outofmemoryerror-exception-java/
周志明.《深入理解Java虚拟机》.机械工业出版社,2011

推荐阅读:

ZooKeeper集群安装

俯拾皆是的Java注解,你真的get了吗?

“八面玲珑”的ZooKeeper入门介绍

责无旁贷:超酷的责任链模式

聚沙成塔:聊聊建造者模式

公司裁员,码农竟然成了“帮凶”?(剧情杜撰)

原文始发于微信公众号(互联网全栈架构):臭名昭著,怙恶不悛的OOM,到底是什么?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/173571.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!