专注于快乐的事情

参看线程堆栈-jstack

#jstack介绍

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。

线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。

另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

命令介绍

基本用法

jstack -l 1086 > /tmp/t.txt

基本参数

-F当’jstack [-l] pid’没有相应的时候强制打印栈信息
-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表。
-m打印java和native c/c++框架的所有栈信息。
-h | -help打印帮助信息。
pid 需要被打印配置信息的java进程id,可以用jps查询。

常见的jstack分析步骤

1.linux下使用top或者pidstat命令查看进程消耗情况

假设进程号为26147,(通过 pidstat -p 26147 -t -u 1 找到线程号26160 ),也可以通过top -H

2.

3.进行分析
printf %x 26160

得到311f

4.jstack命令定位代码

jstack 26147 | grep 6630 -A 50

也可以先输出到文件,然后进行分析
or jstack -l 26147 > t.txt
grep java.lang.Thread.State t.txt | awk '{print $2$3$4$5}' | sort | uniq -c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f64800d9000 nid=0x709f runnable [0x00007f646b039000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:326)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
- locked <0x00000000e0c13c60> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:482)
- locked <0x00000000e0c08200> (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 <0x00000000e0c081a8> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
- eliminated <0x00000000e0c08200> (a java.io.PrintStream)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
- locked <0x00000000e0c08200> (a java.io.PrintStream)
at CpuAll.run(CpuAll.java:15)
at java.lang.Thread.run(Thread.java:745)

如果判断CpuAll.run方法有问题。

使用jstack进行分析

使用jstack进行分析,需要了解线程的各种状态,可以参考线程基本学习

线程状态

java.lang.Thread.State枚举类中定义了如下几种类型:

  • NEW:线程创建尚未启动。
  • RUNNABLE:包括操作系统线程状态中的Ready和Running,可能在等待时间片或者正在执行。
  • BLOCKED:线程被阻塞。
  • WAITING:不会分配CPU执行时间,直到别的线程显式的唤醒,否则无限期等待。LockSupport.park(),没有设置Timeout参数的Object.wait()和Thread.join(),会导致此现象。
  • TIMED_WAITING:不会分配CPU执行时间,直到系统自动唤醒,不需要别的线程显示唤醒。Thread.sleep(),LockSupport.parkNanos(),LockSupport.parkUntil(),设置了超时时间的Object.wait()和Thread.join(),会让线程进入有限期等待。
  • TERMINATED:线程执行结束

阻塞状态与等待状态的区别是:阻塞状态的线程是在等待一个排它锁,直到别的线程释放该排它锁,该线程获取到该锁才能退出阻塞状态;

而等待状态的线程则是等待一段时间,由系统唤醒或者别的线程唤醒,该线程便退出等待状态。

线程状态转化图如下:

线程的状态图

文件分析

生成的dump文件的内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fb8a80d9000 nid=0x312c runnable [0x00007fb89222b000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:326)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
- locked <0x00000000e0c17848> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:482)
- locked <0x00000000e0c04920> (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 <0x00000000e0c048c8> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
- eliminated <0x00000000e0c04920> (a java.io.PrintStream)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
- locked <0x00000000e0c04920> (a java.io.PrintStream)
at CpuAll.run(CpuAll.java:15)
at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:
- None
1
2
3
4
5
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fb8a80bb800 nid=0x312a runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:
- None

Service Thread:线程的名字
daemon:守护线程
prio=10:线程的优先级(默认是5)
tid:Java的线程Id(线程在当前虚拟机中的唯一标识)
nid:线程本地标识
runnable:线程的状态
[0x00007fb89222b000]:当前运行的线程在堆中的地址范围

#常见情况如下

线程状态为“waiting for monitor entry”

意味着它 在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。
此时线程状态一般都是 Blocked:
java.lang.Thread.State: BLOCKED (on object monitor)

线程状态为“waiting on condition”

说明它在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。
此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。

大量线程在“waiting for monitor entry”

可能是一个全局锁阻塞住了大量线程。
如果短时间内打印的 thread dump 文件反映,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。

大量线程在“waiting on condition”

可能是它们又跑去获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。
所以如果你发现有大量的线程都处在 Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。

线程状态为“in Object.wait()”

说明它获得了监视器之后,又调用了 java.lang.Object.wait() 方法。
每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。
当线程获得了 Monitor,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。
此时线程状态大致为以下几种:
java.lang.Thread.State: TIMED_WAITING (on object monitor);
java.lang.Thread.State: WAITING (on object monitor);

#其他常用命令

统计所有线程分别处于什么状态

grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}' | sort | uniq -c

参考

评论系统未开启,无法评论!