模拟一次 OOM 异常的定位流程
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=你的路径
加完后是这个样子的:
运行结果:
可以看到,由于来不及 GC,在运行了11次循环后,抛出了 OOM 异常,此时根据我们的设定,生成了 java_pid10692.hprof 文件
4. 对快照文件进行分析
对于导出的快照文件,我们需要有一个工具打开来分析,这里我们选用的是 JProfiler
安装 JProfiler 后,快照文件被关联,可以直接打开:
快照记录的信息:
可以看到,Btye 数组创建了11个实例,并且占用了45M左右的空间
我们再进一步观察对象:
可以看到,一个 ArrayList 对象占用了98% 的空间,选中这个对象,查看它的引用关系:
这时候看到大对象是ArrayList里的对象,Retained Heap(深堆)代表对象本身和对象关联的对象占用的内存,Shallow Size(浅堆)代表对象本身占用的内存。这个 ArrayList 对象本身只有 24字节,但是其所有关联的对象占用了实际的高内存。这些就可以说明,肯定有哪里在不断向这个 ArrayList 中添加对象,导致了 OOM。而且显示出是程序的主线程main里面的代码。这也与我们的代码本身预期一致
查看 Graph 关系图可以发现,对象属于方法 main,与预期一致
定位到代码后,就是去找具体的源码啦
5. 总结
这里只是跟简单介绍了一下OOM的排查过程,当然实际项目中情况远复杂与这个,往往要经过一系列工具才能定位到具体的代码,当然你也有可能定位不到。
所以最重要的还是要做好代码审查,系统监控,以及关键步骤的留痕
转载自:https://juejin.cn/post/6998080617539698724