OOM-万恶的内存溢出

1,048 阅读3分钟

Metaspace内存溢出

Metaspace这块区域一般很少发生内存溢出,如果发生内存溢出一般都是因为两个原因:

  • 不懂JVM的运行原理,在上线系统的时候对Metaspace区域直接用默认的参数,即根本不设置其大小这会导致默认的Metaspace区域可能才几十MB而已,此时对于一个稍微大型一点的系统,因为有很多类,还依赖了很多外部的jar包有有很多的类,几十MB的Metaspace很容易就不够
  • 用cglib之类的技术动态生成一些类,一旦代码中没有控制好,导致你生成的类过于多的时候,就很容易把Metaspace给塞满,进而引发内存溢出

模拟案例

maven引入

<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.3.0</version>
</dependency>

java代码

public class Demo3 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test.class);
        enhancer.setUseCache(false);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            if (method.getName().equals("run")) {
                System.out.println("反射执行");
            }
            return method.invoke(o, objects);
        });
        Test test = (Test) enhancer.create();
        test.run();
    }

    static class Test {
        public void run() {
            System.out.println("测试代码。。。。");
        }
    }
}

Caused by: java.lang.OutOfMemoryError: Metaspace

栈内存溢出

如果不停的让线程调用方法,不停的往栈里放入栈桢,此时终有一个时刻,大量的栈桢会消耗完毕这个1MB的线程栈内存,最终就会导致出现栈内存溢出的情况,一般来说,其实引发栈内存溢出,往往都是代码里写了一些bug才会导致的,正常情况下发生的比较少

模拟案例

public class Demo4 {
    public static void main(String[] args) {
        test();
    }
    public static  void test(){
        test();
    }
}

Exception in thread "main" java.lang.StackOverflowError at com.silly.demo.jvm.Demo4.test(Demo4.java:12)

堆内存溢出

有限的内存中放了过多的对象,而且大多数都是存活的,此时即使GC过后还是大部分都存活,所以要继续放入更多对象已经不可能,此时只能引发内存溢出问题。所以一般来说发生内存溢出有两种主要的场景:

  • 系统承载高并发请求,因为请求量过大,导致大量对象都是存活的,所以要继续放入新的对象实在是不行了,此时就会引发OOM系统崩溃
  • 系统有内存泄漏的问题,就是莫名其妙弄了很多的对象,结果对象都是存活的,没有及时取消对他们的引用,导致触发GC还是无法回收,此时只能引发内存溢出,因为内存实在放不下更多对象了

因此总结起来,一般引发OOM,要不然是系统负载过高,要不然就是有内存泄漏的问题

这个OOM问题,一旦你的代码写的不太好,或者设计有缺陷,还是比较容易引发的,所以这个问题也是我们后面要重点分析的。

模拟案例

public class Demo5 {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        while (true) {
            list.add(new Object());
        }
    }

}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:265) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231) at java.util.ArrayList.add(ArrayList.java:462) at com.silly.demo.jvm.Demo5.main(Demo5.java:14)

OOM的时候自动dump内存快照

​ 我们可以让他在OOM时dump一份内存快照,事后我们只要分析这个内存快照,一下就可以知道是哪些可恶的对象占用了所有的内

存,并且还无法释放。此时你就需要在JVM的启动参数中加入如下的一些参数:

-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/usr/local/app/oom