记一次Java应用线程泄漏处理
ps -Tfp <pid>cat /proc/<pid>/stat | awk '{print $20}'ls /proc/<pid>/task | wc? ? ?因为这个应用自己是比较熟悉的了,平时正常情况下线程数基本在450左右,1023肯定是个不正常的情况了,所以心里基本有底已经部分线程失控了。顺手ulimit -a看了下,发现max user processes被设置为1024了。虽然说这个值确实是有点小了,生产环境下基本不可能设置一个这么小的值。但测试环境下倒正好是因为这个相对较小的值才暴露出了问题,如果设置的很大,估计还得跑很久才能暴露出问题。? ? 因为此时部署该Java应用的admin账号已经达到最大线程了,所以没法通过SecureCRT远程登录上去。此时,应该是可以找root账号做下thread dump的,问题基本就清楚了。结果测试冲动了下直接就让root把服务器重启了下,好在环境没变化,跑了一段时间之后问题就再浮现出来了。当然,系统比较熟悉的话,单次thread dump基本就可以确定问题所在了。如果系统不是太熟悉的话,记得在不同的时间点做下thread dump,便于分析出产生的主要线程是什么。 Thread Dump之后,如果问题太明显,看看dump日志基本就可以发现了。如果情况比较复杂,可以试试一个叫TDA的工具,可以看到各状态线程的数量,以及各种类型线程的数量。更有用的是,可以比较多次dump之间线程的变化情况,这个对于比较复杂的情况非常有帮助。 回到这个case,原因还是比较简单的。应用上动态创建了很多与第三方公司的客户端长连接线程,进行双方发送消息,为了进行并发流量统计和控制,在这个客户端线程又创建了一个线程用来定时清空计数。后台有定时任务定时检测各个客户端线程的运行情况,当客户端线程异常结束之后,该定时检测任务会在对应客户端线程的实例上重新new Thread(Runnable),然后start起来。问题就出在客户端线程异常结束的处理上,只是通过finally的方式结束掉了客户端线程,但并没有对清空计数的子线程进行处理,于是后台定时任务重启客户端的时候会重新创建一个清空计数的子线程。当测试环境不太稳定,客户端线程基数较大,又不断出现异常结束的情况下,后台检测客户端状态的定时任务就会大量的重启这些客户端线程,产生大量没有退出、不受控制的清空计数的子线程。
知道原因了,问题也比较简单,具体处理办法就不说了。