JVM内存溢出实战和总结(采用VisualVM工具)
JVM内存溢出实战和总结
一、通用技巧
- 某一天任务进程突然不工作了。查看日志,是昨晚10点就停止作业了。查看进程状态,还活着。查看jstack,没有死锁,还有进程在跑着。
sudo ps -ef | grep java
sudo jstack 进程号
- 怀疑内存溢出,准备dump内存镜像,先往上翻一下日志,grep memory 。找到了关键日志Out of Memory。
导出日志的命令:
jmap -dump:live,format=b,file=/tmp/业务名称-201909021657.bin 进程号
- 去VisualVM官方网下载 visualvm,找一个和自己系统JDK相符的版本下载即可。我在JDK8用visualvm_142版本,win10。
- 然后将下载到本机的dump文件丢给visualvm.exe图标。或者File->load 找到dump文件打开即可。
- 可以看到基本visualvm解析了很多内存信息。
可以看到类的个数和实例个数
基本没啥有用的信息
- 重点是对象池板块和对象查询语言版本
这才是我们诊断溢出的利器。
对象查询语言终端 OQL Console,类似SQL一样,可以帮我们更专业的了解内存情况。不过我们基本用不到,很多溢出场景都可以利用对象池Objects可以看出猫腻了。
- 利用对象池找出可能溢出对象。
点击Count,指示按照实例对象个数做降序。
之后看第一列的类全限定名。
我们知道所有自定类都是由JDK类组成,可以根据类组成结构可以知道对象的个数应该呈现树形结构。
即基础类的个数比较多,自定义类个数比较少。
所以我们不难发现,个数排名靠前都是JDK类库的对象。
换而言之,如果是自定义类混进了前排,那么我们记下他然后逐个排查。混进前排的类,要么是业务特殊性造成,要么是业务代码写的不合理,要么是确实内存溢出了。
本例子我们找到了,两个对象,而且这两个对象是组合关系。
不难发现,他们连个数都是一样,明显泄漏了,如果不放心可以隔一段时间再dump一下,可以了解他的增长速度和存活时间。(这一步我就不做了,因为十有八九是它们泄漏了)
二、业务分析技巧
这里需要自己了解自己的业务代码逻辑,以及一个理论:一个对象没法被回收,那么它一定有强引用(其他引用不会造成OOM)。
而强引用大多都是因为静态变量引用造成了,其他局部变量都在方法块结束就会被回收了。
而常见的造成泄漏的强引用,要么是数组或者是队列,所以在排查溢出代码时候多加关照。
了解这些,找出溢出代码就不难了。
三、总结:
- 个数排名靠前都是JDK类库的对象
- 如果是自定义类混进了前排,那么我们记下他然后逐个排查。混进前排的类,要么是业务特殊性造成,要么是业务代码写的不合理,要么是确实内存溢出了
- 一个对象没法被回收,那么它一定有强引用(其他引用不会造成OOM)
- 强引用大多都是因为静态变量引用造成了,其他局部变量都在方法块结束就会被回收了
- 常见的造成泄漏的强引用,要么是数组或者是队列