JVM内存溢出的情况分为四种:
-
元空间溢出
-
栈内存溢出
-
堆内存溢出
-
直接内存溢出
下面具体讲一下这四种情况。
1. 元空间溢出
原因:
(1)Metaspace 区域内存空间不够,但是还要往这里塞入更多类的时候,就会发生内存溢出的问题。
(2)系统上线时,Metaspace 直接使用的默认的参数,而默认参数往往比较小,很容易不够用。
(3)代码中使用 cglib 之类的技术生成类,但是没有控制好,导致生成的类过多,也容易塞满Metaspace,导致内存溢出。
解决方法:
(1)合理分配 Metaspace 区域
(2)避免无限生成动态类
2. 栈内存溢出
原因:
如果不停的让线程调用方法,不停的往栈里面放入栈帧,最终会有一个时刻,大量栈帧会消耗完这个栈内存,最终就会出现栈内存溢出的情况。
解决方法:
避免不合理的调用递归方法等
3. 堆内存溢出
原因:
(1)系统承载高并发请求,因为请求量过大,导致大量对象是存活的,所以要继续放入新的对象实在不行了,此时就会引发 OOM 系统崩溃。
(2)系统有内存泄漏的问题,就是莫名其妙弄了很多对象,结果对象都是存活的,没有及时取消对他们的引用,导致触发 GC 还是无法回收,此时只能引发内存溢出,因为内存实在放不下更多对象了。
解决方法:
(1)减少对象的生命周期,尽量能快速的将使用完毕的对象进行垃圾回收。
(2)适当调整 -Xms(Java 堆内存的大小)和 -Xmx(Java 堆内存最大大小)这两个 JVM 参数
(3)垃圾回收越早越好
4. 直接内存溢出
原因:
(1)内存设置不合理,导致 DirectByteBuffer 对象一直慢慢进入老年代,导致堆外内存一直释放不掉。
(2)设置了 -XX: +DisableExplicitGC 导致 Java NIO 没法主动提醒去回收掉一些垃圾DirectByteBuffer 对象,同样导致堆外内存无法释放掉。
解决方法:
设置参数:-XX: MaxDirectMemorySize,并及时清理内存