JVM随笔记录9-OOM内存溢出

OOM内存溢出

1.metaspace内存溢出

  metaspace元空间是在jvm参数中是可以设置大小的,之前有讲到元空间是存放类的一些信息,那么如果类信息太多,元空间放不下,那是不是会先出现内存溢出的情况呢,而一旦元空间内存溢出,也就意味着JVM没办法再正常运行下去了,系统直接奔溃,那么什么情况下可能出现元空间内存溢出呢

  1.元空间设置太小,或者是使用的默认的metaspace参数,即根本没有设置元空间大小,可能加载的类太多,导致OOM

  2.通过CGLIB动态创建类对象,如果代码没有控制好,生成的类过多,那就可能把元空间填满,最后导致元空间出现内存溢出问题

 以为模拟metaspace出现OOM的代码示例:

public class MetaspaceOOM {

    public static void main(String[] args) {
        int num = 0;
        while (true){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Demo.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    if(method.getName().equals("init")){
                        System.out.println("初始化之前=====");
                        return methodProxy.invokeSuper(o, objects);
                    }else{
                        return methodProxy.invokeSuper(o, objects);
                    }
                }
            });

            Demo demo = (Demo)enhancer.create();
            demo.init();
            num++;
            System.out.println("创建了多少个子类:" + num);
        }
    }


    static class Demo{
        public void init(){
            System.out.println("初始化操作。。。。。。。。");
        }
    }
}

上述实例中设置的元空间大小为10M,-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M ,以下是运行结果:

 

 可以看出在创建第250个字类的时候出现了java.lang.OutOfMemoryError: Metaspace,元空间内存溢出了。解决办法可以是对enhancer做缓存,不需要一直去创建新的类,比如:

public class MetaspaceOOM {

    static Demo demo = null;

    public static void main(String[] args) {
        int num = 0;

        while (true){
            if(demo != null){
                demo.init();
            }else{
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(Demo.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        if(method.getName().equals("init")){
                            System.out.println("初始化之前=====");
                            return methodProxy.invokeSuper(o, objects);
                        }else{
                            return methodProxy.invokeSuper(o, objects);
                        }
                    }
                });

                demo = (Demo)enhancer.create();
                demo.init();
                num++;
                System.out.println("创建了多少个子类:" + num);
            }
        }

    }


    static class Demo{
        public void init(){
            System.out.println("初始化操作。。。。。。。。");
        }
    }
}

 2.线程的栈内存溢出

  之前文章有讲到,jvm启动后,类加载到内存中,线程访问的时候都会有对应的虚拟机栈,在访问方法是会将方法对应的栈帧压入栈中;另外我们在JVM参数中时可以对每个线程的虚拟机栈的内存大小进行设置的,也就是参数-Xss,一般设置时1M,也就是说线程的虚拟机栈内存是固定大小的,然后每个栈帧在虚拟机栈中是会占用内存的,当然栈帧出栈之后,这块内存也会相应的释放掉,那么如果是虚拟机栈中栈帧数量太多,把栈的空间占满了,那么也就会出现内存溢出了,下面用代码模拟一个栈内存溢出的情况

public class StackOOM {
    public static int i = 0;
    public static void log(){
        i++;
        System.out.println("调用次数:" + i);
        log();
    }

    public static void main(String[] args) {
        log();
    }
}

执行后的结果:

 

 从上面可以看出来,在递归调用了6923次之后,出现了java.lang.StackOverflowError栈内存溢出的情况。

3.堆内存溢出

  最后一种是堆内存溢出,堆分为新生代和老年代,eden区满了之后会进行young gc,young gc之后存活的对象会进入survivor区,如果survivor区太小,放不下存活的对象,那么这些对象就会进入到老年代,老年代满了之后就会进行full gc,如果当前老年代的对象都还是存活的,都有被引用,那么这些对象就无法被回收掉,如果这时候还有对象进入老年代,发现老年代已经没有内存可以存放对象,那么这个时候就会出现堆的内存溢出了,堆内存溢出是开发过程中最常见的一种内存溢出,出现堆内存溢出主要是以下两种场景:

  1.突然的高并发请求,因为请求量过来,导致大量的存活对象进入到老年代,当要继续放对象的时候,发现内存不够出现内存溢出

  2.系统又内存泄漏的问题,创建了大量不必要的对象,同时还对些对象保持这引用,同样把堆的空间沾满后发生内存溢出

下面模拟一个堆内存溢出的场景

public class HeapOOM {

    public static void main(String[] args) {
        int i = 0;
        List<Object> list = new ArrayList<>();
        while (true){
            list.add(new Object());
            i++;
            System.out.println("创建了多少个对象:" + i);
        }
    }
}

上述给出的堆内存是2M,新生代1M,-Xms2M -Xmx2M -Xmn1M,运行结果如下:

 

 可以看出在创建了47427个Object对象后堆内存溢出了。

posted @ 2020-10-22 19:52  码一段代码  阅读(392)  评论(0)    收藏  举报