美文网首页
试着搞懂OOM?

试着搞懂OOM?

作者: 程序员点点 | 来源:发表于2021-01-17 22:20 被阅读0次

    "我的代码OOM了,怎么办?"

    "报什么错?"

    "OOM啊,java.lang.OutOfMemoryError"

    "……"

    OOM大约是Java程序员绕不过去的梗,但OOM应该怎么排查呢?

    什么是OOM

    OOM大家都知道,是内存溢出,通俗的说,就是程序运行需要的内存,虚拟机给不了,所以程序就撂挑子不干了。

    OOM产生原因

    1- 内存分配不足——JVM启动参数指定的内存大小不够
    2- 理论上分配够了,但是程序使用内存超预期——内存泄漏|溢出

    内存泄漏:程序已经使用过的内存没有得到回收,此时这部分内存不能分配给其他人但又不被使用。
    内存溢出:程序需要的内存超过了虚拟机能提供的内存大小

    OOM类型

    OOM类型有很多种,针对不同类型可使用不同的排查手段

    java.lang.OutOfMemoryError:Java heap space

    最常见的OOM类型之一了,堆空间不足,举个栗子,程序运行需要20M,但是实际上JVM只提供了10M

    举个栗子:

    以下代码执行时设置JVM参数:-Xms10m -Xmx10m

    -Xms 初始堆内存 10M

    -Xmx 最大堆内存10M

    public static void main(String[] args) {
        String str = "黄河之水天上来";
        while (true){
            //循环创建字符串对象
            str += str + new Random().nextInt(999999999);
            //从常量池中获取字符串,若不存在,则创建一个字符串放到常量池中
            str.intern();
        }
    }
    

    运行结果:


    image

    java.lang.OutOfMemoryError:GC overhead limit exceeded

    这个异常是指GC回收时间过长,时间过多耗费在GC中,但是回收效果不佳。

    回收过长指的是连续多次超过98%的时间用来做GC,但是只回收了不到2%的堆内存,这样即使在GC清理后的内存也会很快再次填满,迫使GC再次执行,就此形成恶性循环,所以需要抛出异常

    再举个栗子:

    设置JVM参数:-Xms10m -Xmx20m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

    PrintGCDetails 打印详细GC信息

    MaxDirectMemorySize 当Direct ByteBuffer分配的堆外内存到达指定大小后,即触发Full GC

    public static void gcOverHead() {
        int i = 0;
        List<String> list = new ArrayList<>();
        try {
            while (true){
                list.add(String.valueOf(++i).intern());
            }
        }catch (Throwable e){
            e.printStackTrace();
            throw e;
        }
    }
    

    运行结果


    image

    java.lang.OutOfMemoryError: Direct buffer memory

    直接内存溢出

    原因分析:

    直接内存崩溃,此处元空间并不在虚拟机中,而是使用本地内存,与GC无关。

    常见于NIO程序中,使用ByteBuffer来读取和写入数据,这是基于通道channel和缓冲区buffer的IO方式,可以使用Native函数直接分配堆外内存,通过存储在JAVA堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。该方式在某些常见中能提高性能,因为避免了java堆和native堆中来回拷贝数据

    还是举个栗子:

    设置JVM参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

    public static void directBufferMemory() {
        System.out.println("本地内存 = " + (VM.maxDirectMemory() / 1024 / 1024) + "MB");
        try{
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //allocateDirect 分配直接内存
        ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
    

    运行结果:


    image

    相关文章

      网友评论

          本文标题:试着搞懂OOM?

          本文链接:https://www.haomeiwen.com/subject/vhgzaktx.html