一般你去面试的时候,面试官经常会问:请谈谈你对OOM的认识?然后,你可能会说OOM就是out of memory,那如果你只是这么答的话,这可不是面试官想要的答案;面试官又接着问,那你生产过程中有遇到哪些OOM呢?请你说说出常见的OOM问题?这时的你可能是懵的。你知道几种常见的OOM呢?欢迎评论区留言。
常见的OOM总述:
1. StackOverflowError
线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常 。递归调用方法,如果没有方法出口的方法会造成StackOverflowError,或者说如果调用的过深都会抛出,这种错误也比较容易定位。
public class StackOverflowErrorDemo { public static void main(String[] args){ stackOverflowError(); } private static void stackOverflowError() { stackOverflowError(); }}
2. java.lang.OutOfMemoryError: Java heap space
溢出原因:深入理解Java虚拟机书中讲到java堆溢出,Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。import java.util.Random;public class JavaHeapSpaceDemo { public static void main(String[] args){ String str = "seu"; while(true){ str += str + new Random().nextInt(11111111)+new Random().nextInt(22222222); str.intern(); } }}
3. java.lang.OutOfMemoryError:GC overhead limit exceeded
import java.util.ArrayList;import java.util.List;/** GC回收时间长时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了* 不到2%的堆内存,连续多次GC都只回收了不到2%的极端情况下才会抛出。假设不抛出GC overhead limit错误会发生什么情况呢?那就是GC清理的这么点内存很快会再次填满,迫使GC再次执行,这样就形成恶性循环,CPU使用率一直是100%,而GC缺没有任何成果。* */public class GCOverheadDemo { public static void main(String[] args){ int i = 0; List list = new ArrayList<>(); try{ while(true){ list.add(String.valueOf(++i).intern()); } }catch (Throwable e){ System.out.println("***************i:"+i); e.printStackTrace(); throw e; } }}
4. java.lang.OutOfMemoryError:Direct buffer memory
import java.nio.ByteBuffer; /** * 配置参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m * 导致原因: * 写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道,缓冲区的IO方式 * ByteBuffer.alloction(capabicity)分配jvm堆内存,属于GC管辖范围 * ByteBuffer.allocateDirect(capabicity)分配本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度较快。 * 如果不断分配本地内存,但是堆内存很少使用,那么jvm不需要执行GC,DirectByteBuffer对象就不会被回收,这时候堆内存充足 * 但是本地内存已经使用光了,再次分配的时候就会出现OOM异常! * --堆内存不够的时候,会触发fullGC顺带回收 DirectMemory! * MaxDirectMemorySize 默认是64M */public class DirectBufferMemoryDemo { public static void main(String[] args) { System.out.println("配置的MaxDirectMemory:"+(sun.misc.VM.maxDirectMemory()/(double)1024/1024)+"MB"); try{Thread.sleep(3000);} catch (InterruptedException e) { e.printStackTrace();} //-XX:MaxDirectMemorySize=5m 我们配置为5M 但是实际使用是6M ByteBuffer.allocateDirect(6*1024*1024); }}
5. java.lang.OutOfMemoryError:unable to create new native thread
不知道你们生产环境是否会出现这种情况,高并发请求服务器时,经常出现如下异常java.lang.OutOfMemoryError:unable to create new native thread。那出现的原因呢?
1.创建太多的线程了
2.服务器的设置限制了你创建线程的数量了(linux 允许创建的线程数是1024个)
6. java.langOutOfMemoryError:Metaspace
我们知道Java8及以后已经使用Metaspace来替代永久代,Metaspace并不在虚拟机内存中而是使用本地内存。主要还是是加载到内存中的 class 数量太多或者体积太大。这时可以通过JVM参数-XX:MaxMetaspaceSize指定。* JVM参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m * Java8之后的版本使用Metaspace来替代永久代 * Metaspace是方法区在HotSpot中的实现,它与持久带最大的区别在于:Metaspace并不在虚拟机内存中而是使用 * 本地内存,也即在java8中,class metaspace(the virtual machines internal presentation of java class) * ,被存储在叫做Metaspace的native memory * * 永久代(Metaspace)存放以下信息: * 虚拟机加载的类信息 * 常量池 * 静态变量 * 即时编译后的代码 * */public class MetaspaceOOMT { static class OOMTest { } public static void main(String[] args) { int i = 0;//模拟多少次后发生异常 try { while (true) { i++; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, args); } }); enhancer.create(); } } catch (Throwable e) { System.out.println("********多少次后发生了异常:" + i); e.printStackTrace(); } }}
总结:
理解jvm才能更有深度的理解这些异常,对jvm的学习应该贯穿整个java!欢迎大家转载,分享,关注我的公众号,一起学习!
如公众号不方便查看,欢迎关注我的博客:https://blog.csdn.net/zhangkaixuan456/article/details/108083096
关注「Java源码进阶」,获取海量java,大数据,机器学习资料!