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解析了很多内存信息。
    可以看到类的个数和实例个数

JVM内存溢出实战和总结(采用VisualVM工具)_第1张图片
基本没啥有用的信息

  • 重点是对象池板块和对象查询语言版本

JVM内存溢出实战和总结(采用VisualVM工具)_第2张图片

这才是我们诊断溢出的利器。

对象查询语言终端 OQL Console,类似SQL一样,可以帮我们更专业的了解内存情况。不过我们基本用不到,很多溢出场景都可以利用对象池Objects可以看出猫腻了。

  • 利用对象池找出可能溢出对象。

JVM内存溢出实战和总结(采用VisualVM工具)_第3张图片

点击Count,指示按照实例对象个数做降序。

之后看第一列的类全限定名。

我们知道所有自定类都是由JDK类组成,可以根据类组成结构可以知道对象的个数应该呈现树形结构。
即基础类的个数比较多,自定义类个数比较少。

所以我们不难发现,个数排名靠前都是JDK类库的对象。

换而言之,如果是自定义类混进了前排,那么我们记下他然后逐个排查。混进前排的类,要么是业务特殊性造成,要么是业务代码写的不合理,要么是确实内存溢出了。

本例子我们找到了,两个对象,而且这两个对象是组合关系。
JVM内存溢出实战和总结(采用VisualVM工具)_第4张图片

不难发现,他们连个数都是一样,明显泄漏了,如果不放心可以隔一段时间再dump一下,可以了解他的增长速度和存活时间。(这一步我就不做了,因为十有八九是它们泄漏了)

二、业务分析技巧

这里需要自己了解自己的业务代码逻辑,以及一个理论:一个对象没法被回收,那么它一定有强引用(其他引用不会造成OOM)。

强引用大多都是因为静态变量引用造成了,其他局部变量都在方法块结束就会被回收了

常见的造成泄漏的强引用,要么是数组或者是队列,所以在排查溢出代码时候多加关照。

了解这些,找出溢出代码就不难了。

三、总结:

  • 个数排名靠前都是JDK类库的对象
  • 如果是自定义类混进了前排,那么我们记下他然后逐个排查。混进前排的类,要么是业务特殊性造成,要么是业务代码写的不合理,要么是确实内存溢出了
  • 一个对象没法被回收,那么它一定有强引用(其他引用不会造成OOM)
  • 强引用大多都是因为静态变量引用造成了,其他局部变量都在方法块结束就会被回收了
  • 常见的造成泄漏的强引用,要么是数组或者是队列

你可能感兴趣的:(java)