JVM深入浅出(3)--- OOM异常排查
1. OutOfMemoryError异常
除了程序计数器外,运行时数据区都可能发生oomError异常
以下是对于运行时内存各个区域OOM异常的排查逻辑。
1.1 Java堆溢出
首先要排查的是发生了内存泄露还是内存溢出。 通过dump堆转储快照进行分析。
内存泄露:需要被回收的内存因为某些引用关系不能被回收掉
- 如果是内存泄露,则通过GCroots引用链排查,泄露对象是和GCroots相关,导致收集器不能回收
内存溢出:所有的对象都是需要的,只不过堆不足以容纳这些对象了。
- 如果是内存溢出,检查堆的参数和机器的内存对比,以及从代码层面看哪些对象存在时间过程,干预某些对象的生命周期。
1.2 虚拟机栈和本地方法栈溢出
虚拟机栈和本地方法栈主要存在两种异常
- StackOverflow: 当栈的深度超过虚拟机最大允许深度时候出现
- oom error : 栈动态扩容后的内存,不足以支持新的内存分配,则会报oom error
在多线程的场景下,hotspot也可以出现oom error,因为虚拟机给每个线程分配的最大内存是有限的,栈内存也就有限了,出现这种情况时候可以考虑减小堆内存/方法区内存。
1.3 方法区和运行时常量池溢出
运行时常量池
-
JDK7之前,运行时常量池在永久代里,报错是
java.lang.OutOfMemoryError: PermGen space -
JDK7 之后,运行时常量池被移到了堆内存,所以这块的溢出可以和堆内存溢出等同考虑
方法区
- String.intern() 是向字符串常量池添加字符串的方法,当字符串常量池中存在这个字符串,就返回对应引用,如果没有,就在字符串常量池中添加该字符串并返回引用
- 在JDK7之前,常量池在方法区中,该方法是通过拷贝字符串到永久代中常量池,并返回引用
- JDK7之后,常量池在堆中,只需要记录首次出现的实例引用即可。
- 方法区溢出:像cglib这样的操作字节码技术能大量产生动态类,导致方法区溢出。但是在JDK8之后,元空间代替了永久代,虚拟机很难产生方法区溢出异常了。hotspot也提供了一些元空间防御措施
- -XX:MaxMetaspaceSize:设置元空间最大值
- -XX:MetaspaceSize:指定元空间的初始空间大小,以字节为单位,达到该值就会触发垃圾收集 进行类型卸载
1.4 本机直接内存溢出
直接内存溢出一般是在dump文件中看不出什么异常,如果dump文件很小,且使用了直接内存,可以排查一下这方面。

浙公网安备 33010602011771号