前言
分享几个JVM的容易忽视的问题,希望对大家有所帮助
哪些区域会出现OOM?
Java堆溢出
- 我们知道,JVM参数配置中
-Xms
表示JVM
启动时分配的内存、-Xmx
表示JVM
运行过程中最大可用内存。 - 由此可见,随着对象实例增多,超过最大堆分配内存的限制,就会出现OOM。
栈溢出
- 栈内存容量参数由
-Xss
决定,而栈内存取决于栈帧数量,即栈深度,以及每个栈帧的大小。 - 虚拟机栈常见的
2
种异常分为OOM
以及StackOverflowError
,那么,取决于什么情况,会抛相应的异常呢?
取决于栈内存是否支持扩展,HotSpot虚拟机不支持扩展
- 由于
HotSpot
虚拟机不支持扩展,因此OOM
的发生情况:创建线程时就因为无法获得足够内存而出现OOM
,创建线程时内存不足,恰恰原因可能是每个线程的栈分配内存设置过大,在操作系统内存使用状态的影响下发生。 - 例如,递归调用导致栈深度过大、定义大量本地变量增加栈帧中本地变量表长度,超过了
-Xss
所决定的栈内存容量,就会抛出StackOverflowError
。
方法区和运行时常量池溢出
[小插曲]方法区和常量池的关系?
- 运行时常量池是方法区的一部分
- 方法区存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
- 运行时常量池:常量池表,用于存放编译期生成的各种字面量与符号引用。(运行时也可以将新的常量放入池中,例如
String::intern()
)
[小插曲]方法区 == 永久代?
- 可以说,HotSpot虚拟机使用永久代来实现方法区,这两者不等价。
- JDK 7的HotSpot,把原本放在永久代的字符串常量池
String::intern()
、静态变量等移出到堆内存;JDK8完全放弃永久代,改用本地内存的元空间(类型信息也移到了元空间)。
回归正题
- 因此,如果使用
String::intern()
来制造方法区和常量池的OOM
1、JDK6或更早:字符串常量池大小增长,导致永久代的内存大小超过
-XX:MaxPermSize
规定的大小,导致出现PermGen space
出现OOM
。 2、JDK7开始,字符串常量池移入Java堆中,使用String::intern()
报出OOM
也是Java heap space
CGLib产生大量的类填充方法区,制造OOM
- 1、JDK7出现
PermGen space
出现OOM
- 2、JDK8出现
Java heap space
GC Roots指什么?
- 虚拟机栈中引用的对象,各个线程被调用的方法中使用到的参数、局部变量、临时变量。
- 方法区类静态属性引用的对象。
- 方法区常量引用的对象,例如字符串常量池的引用。
- Java虚拟机内部的引用,Class对象、异常对象、系统类加载器。
- 同步锁持有的对象(Synchronized)。