什么是 OOM
OOM,Out of Memory,也就是超出了预设内存。
java.lang.OutOfMemoryError,官方说明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是说,当 JVM 没有足够的内存来为对象分配空间,并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
为什么会 OOM
主要原因有两点
- 分配内存不足,可用通过调整启动 VM 参数控制。
- 程序逻辑问题,占用过多内存并且使用完没有及时释放,造成内存泄露或者内存溢出。
其中:
内存泄漏:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。(往往是编码不当导致)
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。
JVM 内存模型
上面频繁提到的内存指的就是 JVM 内存模型。
JVM 在运行会管理以下的内存区域:
- 程序计数器:当前线程执行的字节码的行号指示器,线程私有,记录每个线程当前执行字节码的位置,因为会有多个线程来并发执行各种不同的代码,所有每个线程会有自己的一个程序计数器,专门记录这个线程目前执行到哪一条字节码指令,也就是线程私有;
- java 虚拟机栈:保存方法内局部变量数据的区域,线程私有。线程执行了一个方法,那么就会为这个方法调用创建对应的一个栈帧。栈帧里有这个方法的局部变量表 、操作数栈、动态链接、方法出口等东西,调用执行任何方法的时候,都会给方法创建栈帧,然后入栈,调用完再出栈;
- 本地方法栈:类似“java 虚拟机栈”,但是为 native 方法的运行提供内存环境,调用本地操作系统里面的一些方法,可能是调用 c 语言写的底层类库提供的内存环境;
- java 堆内存:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。对象按不同的 “生存” 情况分为新生代,老年代等;
- 方法区/Metaspace:JDK 1.8 后叫做 Metaspace,JDK 8 开始把类的元数据放到本地堆内存(native heap)中,这一块区域就叫 Metaspace,详情见深入探究 JVM | 探秘 Metaspace;
- 堆外内存:并不是 JVM 运行时数据区的一部分,可直接访问的内存,比如 NIO 会用到这部分;
OOM 类型
java.lang.OutOfMemoryError: Java heap space
,比较常见,java 堆内存溢出。详情见OutOfMemoryError系列(1): Java heap spacejava.lang.OutOfMemoryError: GC overhead limit exceeded
,程序基本上耗尽了所有的可用内存, GC也清理不了。详情见OutOfMemoryError系列(2): GC overhead limit exceededjava.lang.OutOfMemoryError: PermGen space
,java 永久代溢出,也就是方法区溢出,jdk 7 之前。详情见OutOfMemoryError系列(3): Permgen spacejava.lang.OutOfMemoryError: Metaspace
,相当于 jdk 7以前的java.lang.OutOfMemoryError: PermGen space
。详情见OutOfMemoryError系列(4): Metaspacejava.lang.StackOverflowError
,不会抛 OOM ERROR,但也是比较常见的 Java 内存溢出
查看应用进程id(pid)及启动 VM 参数
jps -v
查找应用进程 id
1 | jps -v |
jinfo -flags <pid>
查看 VM 启动参数
1 | jinfo -flags 9066 |
jmap -heap <pid>
查看 VM 启动参数,以及堆内存情况
1 | jmap -heap 9066 |
上面报错解决 Exception when taking a heapdump using JMAP
解决再运行
1 | jmap -heap 9066 |
获取到的 VM 参数,可关注如下几项参数的情况
1 | -XX:+HeapDumpOnOutOfMemoryError # 是否开启 OOM 时输出 dump 文件,+ 表示开启 |
如果应用没有启用相应的参数输出日志,可以进行以下操作查看相关日志
1 | # 1.查看应用 GC 次数及平均每次 GC 时间 |