配景
某天发明客户情况一直有OOM产生,并且是路线状的内存增长. 较量郁闷.
Abstract
这个文章内里会描写以下几件工作:
1. 在Java中有OOM应该怎么阐明?
2. Java finalizer为什么会激发OOM?
3. 为什么不能利用Thread.stop
Java中产生OOM应该怎么阐明
大大都时候Java都做得足够好. 可是没步伐照旧有大概会有OutOfMemoryError(OOM) 产生. 那么我们应该怎么阐明一个OOM错误呢?
拿到内存转储
方法1:自动转储
当OOM产生时, Java可以自动实验(best effort)生成一个堆转储.只需要你在启动参数中加上如下参数:
-XX:+HeapDumpOnOutOfMemoryError
这种方法生成的文件会在java的事情目次内里, 名字叫 java_pidXXX.hprof
方法2:手动转储
有时候你已经可以发明某个Java历程占用了许多内存了. 是时候手动导出一个堆内存了.
利用jmap. jmap是一个jdk自带的东西, 在jdk/bin下面,
jmap -dump:format=b,file=heap.bin <pid> jmap -F -dump:format=b,file=heap.bin <pid> 强制导出
有一点很重要: 出于兼容性思量,必然利用和你运行JRE**运行版内情同**的jmap东西.
运用东西阐明
常见的内存阐明东西是Eclipse的Memory analyzer 和 Jvisualvm.
Eclispe memory analyzer/ MAT
这个是Eclipse推出的一个可视化内存阐明东西.
内里的许多组件都很好用:
1. histogram 列出每个class有几多instance
2. Leak suspects 列出了东西以为大概有内存泄漏的处所.
好比Leak suspects页面的下图:
从上图可以看出, Finalizer线程占用860M的内存, 约93%的空间.
假如这些尺度的页面不能让你获得你存眷的对象, 可以回收OQL console. 就是上图左上角第4个图标.
回收OQL 我们可以做许多的工作, 好比列出Finalizer工具引用的哪些工具都是啥:
列出所有URLConnection的url:
SELECT f.referent.url.toString() FROM java.lang.ref.Finalizer f WHERE f.referent.toString().startsWith("sun.net.www.protocol.https.HttpsURLConnectionImpl")
mat的oql参考地点:http://help.eclipse.org/neon/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html
更强大的Jvisualvm
jdk自带的jvisualvm也可以阐明堆转储.
它提供了比MAT越发强大的阐明成果.
MAT是无法做到雷同于sql groupby filter, top 之类的操纵的. 可能越发巨大的查询. 可是在jvisualvm我们可以实现很巨大的查询:
好比下面的query可以实现: 我想查察Finalizer所引用的工具中, 前10个instance最多的class都是啥:
var counts = {}; var alreadyReturned = {}; top( filter( sort( map(heap.objects("java.lang.ref.Finalizer"), function (fobject) { var className = classof(fobject.referent) if (!counts[className]) { counts[className] = 1; } else { counts[className] = counts[className] + 1; } return {string: className, count: counts[className]}; }), 'rhs.count-lhs.count'), function (countObject) { if (!alreadyReturned[countObject.string]) { alreadyReturned[countObject.string] = true; return true; } else { return false; } }), "rhs.count > lhs.count", 10);
大抵表明下:
heap.objects(“java.lang.ref.Finalizer”)指定了堆上这个类的所有instance;
在map中, 每个record被映射为具有2个属性的工具:{string:className, count[className]}, 属性string, 代表class名字, 属性count代表它的instance个数;
在排序函数中rhs代表右边的元素, lhs代表左边的元素.
示例输出(不是前面的heap,可是不影响抚玩)
jvisualvm的oql参考地点:https://visualvm.github.io/documentation.html (在内里的OQL部门)
Java Finalizer为啥会激发OOM
从前面的heap已经看到了, 有许多的instance都被Java中的Finalizer线程引用了.
什么是Finalizer