JVM深入浅出(3)--- OOM异常排查

1. OutOfMemoryError异常

除了程序计数器外,运行时数据区都可能发生oomError异常

以下是对于运行时内存各个区域OOM异常的排查逻辑。

1.1 Java堆溢出

首先要排查的是发生了内存泄露还是内存溢出。 通过dump堆转储快照进行分析。

内存泄露:需要被回收的内存因为某些引用关系不能被回收掉

  • 如果是内存泄露,则通过GCroots引用链排查,泄露对象是和GCroots相关,导致收集器不能回收

内存溢出:所有的对象都是需要的,只不过堆不足以容纳这些对象了。

  • 如果是内存溢出,检查堆的参数和机器的内存对比,以及从代码层面看哪些对象存在时间过程,干预某些对象的生命周期。

1.2 虚拟机栈和本地方法栈溢出

虚拟机栈和本地方法栈主要存在两种异常

  1. StackOverflow: 当栈的深度超过虚拟机最大允许深度时候出现
  2. 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文件很小,且使用了直接内存,可以排查一下这方面。

posted @ 2026-04-06 12:39  不会coding的喵酱  阅读(6)  评论(0)    收藏  举报