配景
某天发明客户情况一直有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