1. 概述

随着系统日益增大,代码结构日渐复杂,经过验收测试的系统可能在实际生产环境下表现的一塌糊涂,也可能非常棒。因此通过QA并不能保证系统不会发生内存泄漏,往往流量越大泄露的越快,最后导致系统崩溃。比如在某个时间点系统一直出现TimeOut、或者系统突然处理速度急剧下降等问题。对于开发人员就非常棘手了,很多人根本一头雾水,基本上就是拍脑袋瞎猜了。其实发现此类问题定位的技术主要有内存泄漏定位、线程堆栈分析等。

线程堆栈概述

线程堆栈也就是所谓的线程调用栈(都是独立的),在Java线程堆栈式JVM线程状态的一个瞬时快照,快照包含了当前时刻所有线程的运行状态,包括每一个线程的调用栈,锁的持有等信息。每个虚拟机都提供了Thread Dump的后门帮助我们导出堆栈信息。借助线程堆栈会帮助我们迅速地缩小问题的范围, 找到突破口, 命中目标 。

线程堆栈一般包含的信息:

1、线程名字,ID,线程的数量

2、线程运行状态、锁的状态(锁被哪个线程持有,哪个线程在等待)

3、函数调用层级关系。包括了完整类名,方法名,源代码行数

线程堆栈信息的多少,依赖于你系统的复杂度,借助堆栈信息,可分析很多问题,如线程死锁、锁竞争、死循环、识别耗时操作等。在多线程场景下进行稳定问题分析和性能分析他是最有效的方法,多数情况甚至无需了解系统就可以进行相应分析。

当然,线程堆栈也不是万能的,因为其是瞬时快照,所以对已消失没有留下痕迹的信息,是无法追踪历史的。这种情况只能借助于监控系统和日志进行分析。如连接池中的连接被哪些线程使用了没有被释放这类问题。其实也就是说线程堆栈分析的都是非功能性的问题。

线程堆栈善于分析以下问题:

1、系统无缘无故CPU过高

2、系统挂起,无响应

3、系统运行越来越慢

4、线程死锁、死循环、饿死等。

5、由于线程数量太多导致系统失败(如无法创建线程等)

输出堆栈

1、unix/Linux 使用kill -3 pid 进行转储

2、可以使用VisualVM等直接查看转储

3、JDK1.5以上的可以使用Thread.getStackTrace() 控制堆栈自动打印