从JVM heap dump里查找没有关闭文件的引用
背景
最近排查一个文件没有关闭的问题,记录一下。
哪些文件没有关闭是比较容易找到的,查看进程的fd(File Descriptor)就可以。但是确定fd是在哪里被打开,在哪里被引用的就复杂点,特别是在没有重启应用的情况下。
在JVM里可以通过heap dump比较方便地反查对象的引用,从而找到泄露的代码。
以下面简单的demo为例,Demo会创建一个临时文件,并且没有close掉:
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:
$ ls -alh /proc/11278/fd/ total 0 dr-x------ 2 admin users 0 Jun 30 18:20 . dr-xr-xr-x 8 admin users 0 Jun 30 18:20 .. lrwx------ 1 admin users 64 Jun 30 18:20 0 -> /dev/pts/0 lrwx------ 1 admin users 64 Jun 30 18:20 1 -> /dev/pts/0 lr-x------ 1 admin users 64 Jun 30 18:24 11 -> /dev/urandom lr-x------ 1 admin users 64 Jun 30 18:24 12 -> /tmp/test7607712940880692142ttt
对进程进行heap dump
使用jmap命令:
jmap -dump:live,format=b,file=heap.bin 11278
通过OQL查询java.io.FileDescriptor
对象
对于每一个打开的文件在JVM里都有一个java.io.FileDescriptor
对象。查看下源码,可以发现FileDescriptor
里有一个fd
字段:
public final class FileDescriptor { private int fd;
所以需要查找到fd等于12的FileDescriptor
,QOL语句:
select s from java.io.FileDescriptor s where s.fd == 12
使用VisualVM里的OQL控制台查询
在jdk8里自带VisualVM,jdk9之后可以单独下载:https://visualvm.github.io/
把heap dump文件导入VisualVM里,然后在“OQL控制台”查询上面的语句,结果是:
再可以查询到parent,引用相关的对象。
使用jhat查询
除了VisualVM还有其它很多heap dump工具,在jdk里还自带一个jhat工具,尽管在jdk9之后移除掉了,但是个人还是比较喜欢这个工具,因为它是一个web接口的。
jhat -port 7000 heap.bin
访问 http://localhost:7000/oql/ ,可以在浏览器里查询OQL:
打开链接可以查看具体的信息
总结
- 先找出没有关闭文件的fd
- 从heap dump里据fd找出对应的
java.io.FileDescriptor
对象,再找到相关引用
链接
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Python全栈工程师
ParisGabriel Python 入门基础 print(“hello world”)变量 : 存储信息的,日后被调用、修改操作常量: 固定不变的量,字母大写命名规则:1. 字母数字下划线组成2. 不能以数字开头,不能含有特殊字符和空格3. 不能以保留字命名4. 不能以中文命名5. 定义的变量名应该有意义6. 驼峰式命、 下划线分割单词7. 变量名区分大小写a=1b=2if a<b: print("Yes") print("Yes") print("Yes") print("Yes")else: print("No")a=1b=2 if a>b: print("Yes") elif a==b: print("第三") else: print("any") if 条件1: 自拍elif 条件2: 蹦 else: 跳舞 # 单行注释'''多行注释'''""" 多行注释 """ input() "abc" + "qwe" file.py 文件的扩展名:.py : python的程序文件.txt : 文本文件pdf chm html doc xml xls ppt jp...
- 下一篇
年中终结
简单总结: 7月2号,去年这个时候刚到北京,转眼一年一过,时间匆匆! 这一年发生很多事情,有生活有工作的,主要谈谈工作吧。 找工作还算顺利吧,7月2号到北京,找了一星期,7月11号就正式入职了。 刚开始对开发流程比较不熟悉,有些慢,一个简单项目过后,接下来就是“复制->粘贴”了。 在不断学习,又学习了进程通信、网络编程,这块记得多而且用得少,所以要勤看;现在还在学习C++,之前想看数据结构,但觉得C写数据结构有些麻烦,确实如此,C++在数据结构、字符串等许多方面,都比C简单好用些。 但觉得学的杂没有系统化学习,而且缺少项目经验,真的项目经验真的很重要,学很多但不做项目,记不住而且不能发现问题,所以平台很重要,能让你提高很快!!! 接下来的一年,要有系统化深入的学习,自己的不足还有很多,继续加油,为近期的梦想奋斗!!! 对以下几个方面做总结: 现状 想成为什么样的人 做了什么有意义的事,距离目标还有多远 认知上有什么进步 有没有更认识自己 有什么后悔的事 自己需要反复看的话 现状: 依然在去年毕业的公司,想过换工作,但觉得比较麻烦,换个工作还要换住的地方; 想成为什么样的人: 想成...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境