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对象后堆内存溢出了。

浙公网安备 33010602011771号