【JVM 学习笔记 06】:内存溢出OOM及解决
- 一、什么是内存溢出?在哪些区域会发生内存溢出?
- 1.1 Metaspace区域是如何触发内存溢出的及其原因?
- 1.2 线程的栈内存到底是因为什么原因会导致溢出?
- 1.3 堆内存的内存溢出
一、什么是内存溢出?在哪些区域会发生内存溢出?
所谓的JVM OOM内存溢出就是内存有限,放不下对象导致内存溢出的。
一旦你的系统代码不停的往JVM内存里塞入大量的东西,JVM实在是放不下之后,JVM就会告诉你,OutOfMemory,内存溢出了,不能工作了。
通常而言,内存溢出这个问题可能对你的系统是毁灭性的打击,他代表你的JVM内存不足以支撑你的代码的运行
所以一旦发生这个情况,就会导致你的系统直接停止运转,甚至会导致你的JVM进程直接崩溃掉,进程都没了!
这个时候对于线上看起来的场景就是,用户突然发现很奇怪,为什么点击APP、点击网页,都没反应了呢?
第一块可能发生OOM的区域,就是用来存放类信息的Metaspace区域。
第二块可能发生OOM的区域,就是每个线程的虚拟机栈内存。
第三块可能发生内存溢出的区域,就是堆内存空间!
1.1 Metaspace区域是如何触发内存溢出的及其原因?
如下两个参数就是用来设置Metaspace区域大小的:
-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512m
所以实际上来说,在一个JVM中,Metaspace区域的大小是固定的,比如512MB。
那么一旦JVM不停地加载类,加载了很多很多的类,然后Metaspace区域放满了,此时会触发Full GC,连带着回收Metaspace里的类。
由于类的回收条件比较苛刻,那么一旦回收不了多少类,此时你的JVM还在拼命的加载类放到Metaspace里去,直接就会引发内存溢出的问题。因为此时Metaspace区域的内存空间不够了。一旦发生了内存溢出就说明JVM已经没办法继续运行下去了,此时可能你的系统就直接崩溃了,这就是Metaspace区域发生内存溢出的 一个根本的原理。
Metaspace这块区域一般很少发生内存溢出,如果发生内存溢出一般都是因为两个原因:
第一种原因:
在上线系统的时候对Metaspace区域直接用默认的参数,即根本不设置其大小。
这会导致默认的Metaspace区域可能才几十MB而已,此时对于一个稍微大型一点的系统,因为他自己有很多类,还依赖了很多外部的jar包有有很多的类,几十MB的Metaspace很容易就不够了。
通常来说,有经验的工程师上线系统往往会设置对应的Metaspace大小,推荐的值在512MB那样,一般都是足够的。
第二种原因,就是很多人写系统的时候会用cglib之类的技术动态生成一些类,一旦代码中没有控制好,导致你生成的类过于多的时候,就很容易把Metaspace给塞满,进而引发内存溢出。
1.2 线程的栈内存到底是因为什么原因会导致溢出?
每个线程的虚拟机栈的大小是固定的,比如可能就是1MB,然后每次这个线程调用一个 方法,都会将本次方法调用的栈桢压入虚拟机栈里,这个栈桢里是有方法的局部变量的。
如果不停的让线程调用方法,不停的往栈里放入栈桢,此时终有一个时刻,大量的栈桢会消耗完毕这个1MB的线程栈内存,最终就 会导致出现栈内存溢出的情况。
一个线程不停的调用同一个方法,即使是同一个方法,每一次方法调用也会产生一个栈桢压入栈里,比如说对递归循环 sayHello()进行100次调用,那么就会有100个栈桢压入中。最终一定会消耗掉线程的栈内存,引发内存溢出。
所以一般来说,其实引发栈内存溢出,往往都是代码里写了一些bug才会导致的,正常情况下发生的比较少。
1.3 堆内存的内存溢出
有限的内存中放了过多的对象,而且大多数都是存活的,此时即使GC过后还是大部分都存活,所以要继续放入更多对象已经不可能 了,此时只能引发内存溢出问题。
所以一般来说发生内存溢出有两种主要的场景:
系统承载高并发请求,因为请求量过大,导致大量对象都是存活的,所以要继续放入新的对象实在是不行了,此时就会引发OOM系统 崩溃
系统有内存泄漏的问题,就是莫名其妙弄了很多的对象,结果对象都是存活的,没有及时取消对他们的引用,导致触发GC还是无法回 收,此时只能引发内存溢出,因为内存实在放不下更多对象了
因此总结起来,一般引发OOM,要不然是系统负载过高,要不然就是有内存泄漏的问题
这个OOM问题,一旦你的代码写的不太好,或者设计有缺陷,还是比较容易引发的。