从 JVM heap dump 里查找没有关闭文件的引用
(点击上方公众号,可快速关注)
来源:Hengyunabc ,
hengyunabc.github.io/jvm-heap-dump-find-fd/
背景
最近排查一个文件没有关闭的问题,记录一下。
哪些文件没有关闭是比较容易找到的,查看进程的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对象,再找到相关引用
链接
ViauslVM
https://visualvm.github.io/
Object Query Language (OQL)
http://cr.openjdk.java.net/~sundar/8022483/webrev.01/raw_files/new/src/share/classes/com/sun/tools/hat/resources/oqlhelp.html
【关于投稿】
如果大家有原创好文投稿,请直接给公号发送留言。
① 留言格式:
【投稿】+《 文章标题》+ 文章链接
② 示例:
【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/
③ 最后请附上您的个人简介哈~
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
- HttpClient4.X 升级 入门 + http连接池使用
- 【JVM系列】一步步解析java执行内幕
- 揭秘JAVA JVM内幕(不合适初学者)
- 记一次线上商城系统 Tomcat、JVM 高并发的优化
- Java NIO:Buffer、Channel 和 Selector
- JVM 如何操作缓冲区
- 压缩20M文件从30秒到1秒的优化过程
- 快三做代理真的能赚钱吗有什么技巧可言学
- 停滞数年后,ElasticJob 携首个 Apache 版本 3.0.0-alpha 回归!
- SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录
- 为什么阿里巴巴禁止使用Apache Beanutils进行属性的copy?
- 无锁缓存,每秒10万并发,究竟如何实现?
- 事务的基本概念,Mysql事务处理原理
- 七夕之夜,如何保证私密信息不泄露?
- 终于有人把 Docker 讲清楚了,别再说不会 Docker 了!
- 亿级数据从 MySQL 到 Hbase 的三种同步方案与实践!
- 百亿关系链,架构如何设计?
- 淘宝大秒系统设计详解
- Nginx实践:(1) Nginx安装及日志配置
- Nginx实践:(2) Nginx语法之localtion
- HttpClient4.X 升级 入门 + http连接池使用
- 【JVM系列】一步步解析java执行内幕
- 揭秘JAVA JVM内幕(不合适初学者)
- 记一次线上商城系统 Tomcat、JVM 高并发的优化
- Java NIO:Buffer、Channel 和 Selector
- JVM 如何操作缓冲区
- 压缩20M文件从30秒到1秒的优化过程
- 快三做代理真的能赚钱吗有什么技巧可言学
- 停滞数年后,ElasticJob 携首个 Apache 版本 3.0.0-alpha 回归!
- SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录
- 为什么阿里巴巴禁止使用Apache Beanutils进行属性的copy?
- 无锁缓存,每秒10万并发,究竟如何实现?
- 事务的基本概念,Mysql事务处理原理
- 七夕之夜,如何保证私密信息不泄露?
- 终于有人把 Docker 讲清楚了,别再说不会 Docker 了!
- 亿级数据从 MySQL 到 Hbase 的三种同步方案与实践!
- 百亿关系链,架构如何设计?
- 淘宝大秒系统设计详解
- Nginx实践:(1) Nginx安装及日志配置
- Nginx实践:(2) Nginx语法之localtion