这篇文章将为大家详细讲解有关如何从JVM heap dump里查找没有关闭文件的引用,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
最近排查一个文件没有关闭的问题,记录一下。
哪些文件没有关闭是比较容易找到的,查看进程的fd(File Descriptor)就可以。但是确定fd是在哪里被打开,在哪里被引用的就复杂点,特别是在没有重启应用的情况下。
在JVM里可以通过heap dump比较方便地反查对象的引用,从而找到泄露的代码。
以下面简单的demo为例,Demo会创建一个临时文件,并且没有close掉:
1
2
3
4
5
6
7
8
9
10
11
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.IOException;
public
class
Test {
public
static
void
main(String[] args)
throws
IOException {
File tempFile=File.createTempFile(
"test"
,
"ttt"
);
FileInputStream fi=
new
FileInputStream(tempFile);
System.in.read();
}
}
通过文件名查找对应的fd
进程打开的文件在OS里有对应的fd(File Descriptor),可以用lsof命令或者直接在linux下到/proc
目录下查看。
以demo为例,可以找到test文件的fd是12:
1
2
3
4
5
6
7
8
$
ls
-alh
/proc/11278/fd/
total 0
dr-x------ 2 admin
users
<代码> 0 6月30日18:20。代码>
<代码> dr-xr-xr-x 8用户管理代码> <代码> 代码>,<代码> 0 6月30日18:20 . .代码>
<代码> lrwx - - - - - - 1用户管理代码> <代码> 代码> <代码> 64年6月30日18:20 0→代码> <代码>/dev/pts/0 代码>
<代码> lrwx - - - - - - 1用户管理代码> <代码> 代码> <代码> 64年6月30日18:20 1→代码> <代码>/dev/pts/0 代码>
<代码> lr-x - - - - - - 1用户管理代码> <代码> 代码> <代码> 64年6月30日18:24 11→代码> <代码>/dev/urandom 代码>
<代码> lr-x - - - - - - 1用户管理代码> <代码> 代码> <代码> 64年6月30日18:24 12→代码> <代码>/tmp/test7607712940880692142ttt 代码>
对进程进行堆转储
使用jmap命令:1
<代码> jmap您:生活,代码> <代码> 代码> <代码>=b格式,文件代码> <代码> 代码> <代码>=堆。本11278年代码>
通过OQL查询<代码> java.io.FileDescriptor> 代码对象
对于每一个打开的文件在JVM里都有一个<代码> . io .FileDescriptor> 代码对象。查看下源码,可以发现<代码> FileDescriptor> 代码里有一个<代码> fd> 代码字段:
1 2
<代码>公共代码> <代码>最后代码> <代码> 代码> <代码> FileDescriptor类{代码>
<代码>,,,,代码> <代码>私人int 代码> <代码> 代码> <代码> fd; 代码>
所以需要查找到fd等于12日的<代码> FileDescriptor> 代码,生命质量语句:1
<代码>从io选择年代。FileDescriptor在年代。fd==12 代码>
使用VisualVM里的OQL控制台查询
在jdk8里自带VisualVM, jdk9之后可以单独下载:https://visualvm.github。io/
把堆转储文件导入VisualVM里,然后在“OQL控制台“查询上面的语句,结果是: