Java线程堆栈分析

线程堆栈也称作线程调用堆栈。Java线程堆栈是虚拟机中线程(包括锁)状态的一个瞬间快照,即系统在某个时刻所有线程的运行状态,包括每一个线程的调用堆栈,锁的持有情况等信息。

线程堆栈的信息都包含:

  1. 线程的名字,ID,线程的数量等。
  2. 线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程再等待锁等)。
  3. 调用堆栈(即函数的调用层次关系)。调用堆栈包含完整的类名,所执行的方法,源代码的行数。

借助线程堆栈,可以分析许多问题,如线程死锁、锁争用、死循环、识别耗时操作等等。在多线程场合下的稳定性问题分析和性能问题分析,线程堆栈分析是最有效的方法,在多数情况下甚至无需对系统了解就可以进行相应的分析。
线程堆栈最善于分析如下类型的问题:
• 系统无缘无故CPU过高。
• 系统挂起,无响应。
• 系统运行越来越慢。
• 性能瓶颈(如无法充分利用CPU等)
• 线程死锁、死循环,饿死等。
• 由于线程数量太多导致系统失败(如无法创建线程等)。

输出线程状态

Java虚拟机提供了线程转储(Thread dump)的后门,通过这个后门,可以将线程堆栈打印出来。
我们这里拿Linux做例子,写了一个简单的java程序在Linux一直运行着:

  • kill -3 [java pid]
    不会在当前终端输出,它会输出到代码执行的或指定的地方去。比如,kill -3 tomcat pid, 输出堆栈到log目录下。
  • Jstack [java pid]
    这个比较简单,在当前终端显示,也可以重定向到指定文件中。
  • JvisualVM:Thread Dump
    不做说明,打开JvisualVM后,都是界面操作,过程还是很简单的。

解读线程状态

dump的线程信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"main" prio=10 tid=0x00007f730c008800 nid=0x63eb runnable [0x00007f73117c6000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:345)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
- locked <0x00000000cc407ab8> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:482)
- locked <0x00000000cc407a98> (a java.io.PrintStream)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
- locked <0x00000000cc407b90> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.newLine(PrintStream.java:546)
- eliminated <0x00000000cc407a98> (a java.io.PrintStream)
at java.io.PrintStream.println(PrintStream.java:737)
- locked <0x00000000cc407a98> (a java.io.PrintStream)
at test.fun2(test.java:19)
at test.fun1(test.java:12)
at test.main(test.java:6)

线程堆栈里面的最直观的信息是当前线程的调用上下文,即从哪个函数中调用到哪个函数中(从下往上看),正执行到哪个类的哪一行.

解读

  • “main” prio=10 tid=0x00007f730c008800 nid=0x63eb runnable [0x00007f73117c6000]
    “main” - 线程名称
    prio=10 - 线程优先级
    tid=0x00007f730c008800 - 线程ID
    nid=0x63eb - 对应本地线程ID
    runnable - 线程状态
    [0x00007f73117c6000] - 线程占用内存地址

    “本地线程”是指该Java线程所对应的虚拟机中的本地线程。

    1. ps –ef|grep java:获取java进程id
    2. 使用pstack 获得Java虚拟机的本地线程的堆栈
  • at test.fun2 (test.java:19)
    test - 类名
    fun2 - 方法名
    test.java - 源代码文件
    19 - 所在行号

瓶颈分析

详细参考文章:分析

唐胡璐 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
分享创造价值,您的支持将鼓励我继续前行!