java内存泄露如何排查

问题:某jboss服务器经过一段时间运行后内存使用率持续增长至90+%,根据现象判断存在内存泄露

一、heapdump文件分析

java heapdump文件获取命令:

/xxx/openjdk-1.8.0_92/bin/jmap -dump:format=b,file=/xxx/my.hprof

使用 Eclipse Memory Analyzer 内存分析工具解析 my.hprof 文件

(1)点击“Leak Suspects”查看内存泄露报告

(2)内存泄露报告显示有17525个thread对象,基本确定是大量thread对象占用过多内存

(3)继续点击详细信息,可看到 线程名称 和 关键类名,确定是系统定时任务相关产生的线程

二、threaddump文件分析

java threaddump文件,线程快照,获取命令:

/xxx/openjdk-1.8.0_92/bin/jstack [java进程id] >> /xxx/jstack.txt

分析文件发现,存在17000+的“pool-*-thread-*”的thread堆栈信息,这和heapdump显示内存泄露情况能对的上。

三、代码分析

1、在定时任务代码中,加日志,打印线程名称,确定heapdump和threaddump文件中显示的线程是哪里出现的

(1)打印定时任务每次调用的线程名称:名称基本类似”schedulerFactoryBean_Worker-1“

(2)打印定时任务中使用线程池执行的任务的线程名称:名称基本类似”pool-8-thread-1

(3)打印定时任务调用的异步方法(Async注解修饰)所在的线程名称:名称基本类似”executor-1“

===>可以确定产生内存泄露的是:线程池线程对象(其实如果经验丰富,从线程名称一眼可以看出是线程池线程对象造成的(⊙o⊙)…)

2、分析定时任务调用的类,发现代码中使用线程池有两种方式:

// (1)获取spring管理的线程池:
ExecutorService executorService = SpringUtils.getBean("executorService");

// (2)类成员变量定义:
private ExecutorService executorService = Executors.newFixedThreadPool(5);

3、对上述两种情况,加日志打印分析,发现:

(1)获取spring管理的线程池:其线程名是循环出现的,线程数也不会超出线程池定义的最大线程数

(2)使用类成员变量定义线程池:其线程名是变化的,”pool-8-thread-1“中主要是名称中第1个数字变化,而第二个数字是1-5循环出现的,从这可以得出第1个数字指线程池编号,第2数字指线程编号

===>从这可以看出是有大量线程池被创建,和threaddump文件中显示的情况可以对应上

4、对定时任务调用类,加实例的hashcode打印,发现:

----------- SxxxJobExecuter -- 1251083087
----------- SxxxJobExecuter -- 1463534473
----------- SxxxJobExecuter -- 1487763602

===>hashcode是变化的,说明定时任务的实例每次都是新的,这就说明了,为什么有这么多线程池被创建,问题原因找到。

上述线程池这么使用的原因主要是想,每个定时任务配一个线程池,但没有想到定时任务实例是变化的,造成创建大量线程池,而每个线程池都持有一定量的线程,线程池如果不调用shutdown方法是不会关闭的,线程也不会释放,最终造成内存泄露

5、解决方法

使用第二步分析中的第一种方式定义线程池由spring管理,使用时从spring获取。

 

PS.走错路的分析:之前有考虑是否是大量的请求造成jboss创建大量线程造成。经过分析调研确定,如jboss、tomcat等都有最大线程数设置,其线程数不会超出这个范围,且线程名称也不同,另外请求量也没有这么大。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值