Java虚拟机内存有好几个运行时数据区会有OOM的异常,如果能够区分根据报错区分出是哪些区域报出来的异常,会更便于定位问题,解决问题。
1.Java堆溢出
原因:由于不断创建对象实例,当对象数量达到了最大堆的容量限制后产生内存溢出异常。
现象:java.lang.OutOfMemoryError: Java heap space
解决方法:
1)首先确认是内存泄露(Memory Leak)还是内存溢出(Memory Overflow);
2)如果是内存泄漏引起的,查看GC Roots引用链,找出为什么无法被垃圾回收的原因;
3)如果是内存溢出,检查虚拟机的堆参数(-Xmx最大值和-Xms最小值),对比物理内存看是否可以调大;
2.虚拟机栈和本地方法栈溢出
由于Hot Spot虚拟机的实现是不区分两者的,所以可以通过-Xss参数来设定栈容量。
java.lang.StackOverflowError:
原因:在单线程下,虚拟机栈容量太小或者定义了大量的本地变量,会抛出SO;
解决方法:增大虚拟机栈容量;
java.lang.OutOfMemoryError: unable to create new native thread
原因:在多线程下,大量创建新线程,会抛出OOM,每个线程的栈分配的内存越大,越容易产生;
解决方法:减少线程产生、降低最大堆、减少栈容量;
3.运行时常量池溢出
原因:代码在运行时创建了大量的常量,超出了常量池上限;
现象:java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
解决方法:通过修改-XX:PermSize和-XX:MaxPermSize参数来修改方法区大小,从而修改常量池大小;
4.方法区溢出
原因:在运行时,ClassLoader动态加载了大量的Class信息,超出方法区上限;
现象:java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass(Native Method)
解决方法:通过修改-XX:PermSize和-XX:MaxPermSize参数来修改方法区大小;
总之,引起OOM原因有很多,但是如果能够清楚了解底层内存存储的原理,就能够根据报错信息快速定位OOM报错原因,从而解决问题!
下面罗列一些经常用到的jvm参数:
所有用到的JVM启动参数:
-Xss2M 设置JVM栈内存大小
-Xms20M 设置堆内存初始值
-Xmx20M 设置堆内存最大值
-Xmn10M 设置堆内存中新生代大小
-XX:SurvivorRatio=8 设置堆内存中新生代Eden 和 Survivor 比例
-XX:PermSize=10M 设置方法区内存初始值
-XX:MaxPermSize=10M 设置方法区内存最大值
-XX:MaxDirectMemorySize=10M 设置堆内存中新生代大小