文章目录
- 内存泄漏
- 发现问题
- top
- VisualVM
- Arthas
- 原因分析
- 代码层面
- 并发请求
- 诊断问题
- MAT原理 –支配树
- 获取运行时快照
内存泄漏
内存泄漏(memory leak):在Java中如果不再使用一个对象,但是该对象依然在GC ROOT的引用链上, 这个对象就不会被垃圾回收器回收,这种情况就称之为内存泄漏。
内存泄漏绝大多数情况都是由堆内存泄漏引起的。少量的内存泄漏可以容忍,但是如果发生持续的内存泄漏,就像滚雪球雪球越滚越大,不管有多大的内存迟 早会被消耗完,最终导致的结果就是内存溢出。但是产生内存溢出并不是只有内存泄漏这一种原因。
发现问题
top
在linux中使用
top
命令可以查看内存占用情况,-m
按内存使用量降序
VisualVM
VisualVM是多功能合一的Java故障排除工具并且他是一款可视化工具,整合了 命令行 JDK 工具和轻量级分析功能,功能非常强大。
下载地址:https://visualvm.github.io/
Arthas
Arthas是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、 gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断, 包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升 线上问题排查效率。
官网:https://arthas.aliyun.com/
原因分析
代码层面
这里列举两种常见的可能导致内存泄漏的代码
1)如果仅仅使用手动创建的线程,就算没有调用ThreadLocal的remove方法清理数据,也不会产生内存泄漏。因为当线程被回收时,ThreadLocal也同样被回收。但是如果使用线程池就不一定了,如何线程未被回收则ThreadLocal对象会持续保留在内存中导致内存泄漏,所以线程方法执行完,一定要调用ThreadLocal中的remove方法清理对象。
2)如果大量的数据在静态变量中被长期引用,数据就不会被释放,如果这些数据不再使用,就可能造成内存泄漏,所有尽量减少将对象长时间的保存在静态变量中,如果不再使用,必须将对象删除(比如在集合中)或者将静态变量设置为null。
并发请求
并发请求问题指的是用户通过发送请求向Java应用获取数据,正常情况下Java应用将数据返回之后,这部分数据就 可以在内存中被释放掉。
但是由于用户的并发请求量有可能很大,同时处理数据的时间很长,导致大量的数据存在于内存中,最终超过了内存的上限,导致内存溢出。
诊断问题
当堆内存溢出时,需要在堆内存溢出时将整个堆内存保存下来,生成内存快照(Heap Profile)文件。
生成内存快照的Java虚拟机参数:-XX:+HeapDumpOnOutOfMemoryError
:发生OutOfMemoryError错误时,自动生成hprof内存快照文件。-XX:HeapDumpPath=
:指定hprof文件的输出路径。
获取到hprof文件后可使用MAT(https://eclipse.dev/mat/downloads.php)分析工具分析内存泄漏的根源
MAT原理 –支配树
MAT提供了称为支配树(Dominator Tree)的对象图。支配树展示的是对象实例间的支配关系。在对象引用 图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。
支配树中对象本身占用的空间称之为浅堆(Shallow Heap)。
支配树中对象的子树就是所有被该对象支配的内容,这些内容组成了对象的深堆(Retained Heap),也称 之为保留集(Retained Set ) 。深堆的大小表示该对象如果可以被回收,能释放多大的内存空间。
获取运行时快照
使用
jmap -dump:live,format=b,file=文件路径和文件名 进程ID
可获取当前进程的内存快照也可使用通过arthas的heapdump命令导出
在程序员开发用的机器内存范围之内的快照文件,直接使用MAT打开分析即可。但是经常会遇到服务器上的 程序占用的内存达到10G以上,开发机无法正常打开此类内存快照,此时需要下载服务器操作系统对应的 MAT。