JVM利用 MAT 工具实战分析内存泄漏(十)
今天 JVM利用 MAT 工具实战分析内存泄漏
1、通过程序做一份
OOM
之前的
dump
日志。例如:
java -jar -XX:+HeapDumpOnOutOfMemoryError jvm-1.0-SNAPSHOT.jar
ab -c 10 -n 1000 http://127.0.0.1:8080/jvm/mat
2、程序在
OOM
之前导出了
dump
日志,如下图:
3、通过MAT工具分析:
在这里我们就可以很明显地查看到是 ThreadLocal
这块的代码出现了问题。
4、原因分析
ThreadLocal
是基于
ThreadLocalMap
实现的,这个
Map
的
Entry
继承了
WeakReference
,而
Entry
对象中的
key
使用了
WeakReference
封装,也就是说
Entry 中的 key
是一个弱引用类型,而弱引用类型只能存活在下次
GC
之前。
当把
threadlocal
变量置为
null
以后,没有任何强引用指向
threadlocal
实例,所以
threadlocal
将会被
gc
回收。
当发生一次垃圾回收,
ThreadLocalMap
中就会出现
key
为
null
的
Entry
,就没有办法访问这些
key
为
null
的
Entry
的
value
,如果当前线程再迟迟不结束的话(肯定不会结束),这些 key
为
null
的
Entry
的
value
就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
,而这块
value
永远不会被访问到了,所以存在着内存泄露。如下图:
只有当前
thread
结束以后,
current thread
就不会存在栈中,强引用断开,
Current Thread
、
Map value
将全部被
GC
回收(但是这种情况很难)。最好的做法是不在需要使用 ThreadLocal
变量后,都调用它的
remove()
方法,清除数据。
5、总结
可以看到,上手
MAT
工具是有一定门槛的,除了其操作模式,还需要对我们前面介绍的理论知识有深入的理解,比如
GC Roots
、各种引用级别等。 如果不能通过大对象发现问题,则需要对快照进行深入分析。使用柱状图和支配树视图,配合引入引出和各种排序,能够对内存的使用进行整体的摸底。 由于我们能够看到内存中的具体数据,排查一些异常数据就容易得多。
上面这些问题通过分析业务代码,也不难发现其关联性。问题如果非常隐蔽,则需要使用
OQL
等语言,对问题一一排查、确认。