likes
comments
collection
share

模拟一次 OOM 异常的定位流程

作者站长头像
站长
· 阅读数 42

1. 前言

这个实战,倒不是说直接盘公司的环境,头不至于这么铁

OOM全称是OutOfMemory,官网翻译过来是:JVM由于没有足够的内存来为对象分配空间,垃圾回收器也已经没有空间可回收时,就会抛出这个错误

本文通过在本地模拟一次 OOM 异常来讲解 JVM 如何通过工具来定位 OOM 的发生的原因

2. JVM 调优相关工具

2.1 JDK 工具

JDK 自身带了很多工具,来帮你监控系统和排查内存性能问题,这谢工具都存放在 bin 目录重

工具类型作用
jps命令行JVM 进程状态工具,列出系统上的 JVM 进程
jinfo命令行JVM 信息查看工具,查看 JVM 的各种配置信息
jvisualvm图形界面综合的 JVM 监控工具,查看 JVM 的基本情况、做堆栈的转储、做内存和 CPU 的 Profiling 等
jconsole图形界面JMX 兼容的图形工具,用于监控 JVM 的基本情况,查看 MBean
jstat命令行JVM 统计监控工具,附加到一个 JVM 进程上手机和记录 JVM 的各种性能指标数据
jcmd命令行JVM 命令行调试工具,用于向 JVM 进程发送调试命令
jmap命令行JVM 堆内存分析工具,可以打印 JVM 进程对象的直方图、类加载统计,以及做堆转储操作

2.2 Linux 系统监控工具

除了可以使用 JDK 给的工具之外,我们也可以直接使用 Linux 附带的命令来查看系统资源的使用情况,其实 Linux 上有很多更强大的工具,但是现在的项目很多是部署在容器中,默认的容器只带了一些基础的命令供我们使用,比如 top 命令的加强版 htop

命令说明
top实时显示正在执行进程的 CPU 使用率、内存使用率以及系统负载等信息
vmstat对操作系统的虚拟内存、进程、CPU活动进行监控
pidstat监控指定进程的上下文切换
iostat监控磁盘IO

2.3 常用的第三方工具

  • MAT:Memory Analyzer tool的缩写,由 Eclipse 开发
  • Jprofiler:一款收费的 JVM 性能监控工具,可以很方便的跟 IDEA 进行集成
  • Arthas:阿里巴巴的开源的 Java 诊断工具,深受开发者喜爱,没有图形界面

3. 模拟OOM,生成快照文件

创建一个测试类,输入以下代码

public static void main(String[] args) {
    List<Byte[]> list = new ArrayList<>();
    int count = 0;
    while (true) {
        //每次增加10M的内存空间
        list.add(new Byte[1024 * 1024]);
        count++;
        System.out.println(count);
    }
}

再给这个类加上几个参数,把堆内存空间设置的小一点,这样循环没几次就OOM了

  • -Xms50M 初始堆内存设置为50M
  • -Xmx50M 最大堆内存设置为50M
  • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=具体路径 发生OOM 的话导出 Dump 到指定路径

整个 VM Optional:

-Xms50M -Xmx50M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你的路径

加完后是这个样子的:

模拟一次 OOM 异常的定位流程

运行结果:

模拟一次 OOM 异常的定位流程

可以看到,由于来不及 GC,在运行了11次循环后,抛出了 OOM 异常,此时根据我们的设定,生成了 java_pid10692.hprof 文件

4. 对快照文件进行分析

对于导出的快照文件,我们需要有一个工具打开来分析,这里我们选用的是 JProfiler

安装 JProfiler 后,快照文件被关联,可以直接打开:

模拟一次 OOM 异常的定位流程

快照记录的信息:

模拟一次 OOM 异常的定位流程

模拟一次 OOM 异常的定位流程

可以看到,Btye 数组创建了11个实例,并且占用了45M左右的空间

我们再进一步观察对象:

模拟一次 OOM 异常的定位流程

可以看到,一个 ArrayList 对象占用了98% 的空间,选中这个对象,查看它的引用关系:

模拟一次 OOM 异常的定位流程

这时候看到大对象是ArrayList里的对象,Retained Heap(深堆)代表对象本身和对象关联的对象占用的内存,Shallow Size(浅堆)代表对象本身占用的内存。这个 ArrayList 对象本身只有 24字节,但是其所有关联的对象占用了实际的高内存。这些就可以说明,肯定有哪里在不断向这个 ArrayList 中添加对象,导致了 OOM。而且显示出是程序的主线程main里面的代码。这也与我们的代码本身预期一致

查看 Graph 关系图可以发现,对象属于方法 main,与预期一致

模拟一次 OOM 异常的定位流程

定位到代码后,就是去找具体的源码啦

5. 总结

这里只是跟简单介绍了一下OOM的排查过程,当然实际项目中情况远复杂与这个,往往要经过一系列工具才能定位到具体的代码,当然你也有可能定位不到。

所以最重要的还是要做好代码审查,系统监控,以及关键步骤的留痕

转载自:https://juejin.cn/post/6998080617539698724
评论
请登录