OOM 的全称是 Out Of Memory,那我们的内存区域有哪些会发生 OOM 呢?我们可以从内存区域划分图上,看一下彩色部分。
可以看到除了程序计数器,其他区域都有OOM溢出的可能。但是最常见的还是发生在堆上。
所以 OOM 到底是什么引起的呢?有几个原因:
- 内存的容量太小了,需要扩容,或者需要调整堆的空间。
- 错误的引用方式,发生了内存泄漏。没有及时的切断与 GC Roots 的关系。比如线程池里的线程,在复用的情况下忘记清理 ThreadLocal 的内容。
- 接口没有进行范围校验,外部传参超出范围。比如数据库查询时的每页条数等。
- 对堆外内存无限制的使用。这种情况一旦发生更加严重,会造成操作系统内存耗尽。
典型的内存泄漏场景,原因在于对象没有及时的释放自己的引用。比如一个局部变量,被外部的静态集合引用。
你在平常写代码时,一定要注意这种情况,千万不要为了方便把对象到处引用。即使引用了,也要在合适时机进行手动清理。关于这部分的问题根源排查,我们将在实践课程中详细介绍。
元数据区存放的是什么?会oom吗?
Perm Space中保存的是加载class文件。
会引起OutOfMemory,出现异常可以设置 -XX:PermSize 的大小。
JDK 1.8后,字符串常量不存放在永久代,而是在堆内存中,JDK1.8以后没有永久代概念,而是用元空间替代,元空间不存在虚拟机中,二是使用本地内存。
何时会抛出OutOfMemoryException
并不是内存被耗空的时候才抛出
JVM98%的时间都花费在内存回收
每次回收的内存小于2%
哪些区域可能会发生oom?
jvm常见OOM情况
1.Java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。
2.Java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。
3.java.lang.StackOverflowError, 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。