前言

Stack Trace for Java 显示虚拟机的线程快照
生成虚拟机当前时刻的线程快照(一般是threaddump或者javacore文件),线程快照就是每一条线程正在执行的方法堆栈的集合。

目的

定位线程出现长时间停顿的原因,如线程死锁、死循环等等。

命令格式

  1. jstack [option] vmid
  2. option:
  3. -F 正常输出不响应时,强制输出线程堆栈(程序卡死,无法响应时,就需要此参数)
  4. -l 额外显示锁的附加信息
  5. -m 如果调用到本地方法的话,显示C/C++的堆栈

文件格式

查看的文件就如下所示:

  1. "CommercialStatsThread1" #25 prio=5 os_prio=31 tid=0x00007f98423fa000 nid=0x9f03 waiting on condition [0x000070000f7d3000]
  2. java.lang.Thread.State: TIMED_WAITING (parking)
  3. at sun.misc.Unsafe.park(Native Method)
  4. - parking to wait for <0x0000000741517b70> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  5. at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
  6. at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
  7. at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
  8. at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
  9. at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
  10. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
  11. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  12. at java.lang.Thread.run(Thread.java:748)
  13. "BrokerStatsThread1" #24 prio=5 os_prio=31 tid=0x00007f9843c27000 nid=0x5b03 waiting on condition [0x000070000f6d0000]
  14. java.lang.Thread.State: TIMED_WAITING (parking)
  15. at sun.misc.Unsafe.park(Native Method)
  16. - parking to wait for <0x0000000741477960> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  17. at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
  18. at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
  19. at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
  20. at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
  21. at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
  22. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
  23. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  24. at java.lang.Thread.run(Thread.java:748)

修饰符

  • locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。
  • waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在迚入区等待。
  • waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。
  • parking to wait for <地址> 目标

使用例子

  • jstack pid
  • jstack -l 4089 >1.txt
  • 查找最消耗CPU的例子

查找最耗时CPU

  1. top命令找出消耗CPU高的Java进程及其线程id
    1. 开启线程显示模式(top -H,或是打开top后按H
    2. CPU使用率排序(top缺省是按CPU使用降序,已经合要求;打开top后按P可以显式指定按CPU使用降序)
    3. 记下Java进程id及其CPU高的线程id
  2. 查看消耗CPU高的线程栈:
    1. 用进程id作为参数,jstack出有问题的Java进程
    2. 手动转换线程id成十六进制(可以用printf %x 1234
    3. jstack输出中查找十六进制的线程id(可以用vim的查找功能/0x1234,或是grep 0x1234 -A 20
  3. 查看对应的线程栈,分析问题

命令操作如下:

  1. 1.top查找出哪个进程消耗的cpu高。执行top命令,默认是进程视图,其中PID是进程号
  2. co_ad2 18 0 1817m 776m 9712 S 3.3 4.9 12:03.24 java
  3. co_ad 21 0 3028m 2.5g 9432 S 1.0 16.3 6629:44 ja
  4. 这里我们分析21125这个java进程
  5. 2.top中shift+h 或“H”查找出哪个线程消耗的cpu高
  6. 先输入top,然后再按shift+h 或“H”,此时打开的是线程视图,pid为线程号
  7. co_ad2 15 0 1807m 630m 9492 S 1.3 4.0 0:05.12 java
  8. co_ad2_s 15 0 1360m 560m 9176 S 0.3 3.6 0:46.72 java
  9. 这里我们分析21233这个线程,并且注意的是,这个线程是属于21125这个进程的。
  10. 3.使用jstack命令输出这一时刻的线程栈,保存到文件,命名为jstack.log。注意:输出线程栈和保存top命令快照尽量同时进行。
  11. 由于jstack.log文件记录的线程ID是16进制,需要将top命令展示的线程号转换为16进制。
  12. 4. jstack查找这个线程的信息
  13. jstack [进程]|grep -A 10 [线程的16进制]
  14. 即: jstack 21125|grep -A 10 52f1
  15. -A 10表示查找到所在行的后10行。21233用计算器转换为16进制52f1,注意字母是小写。
  16. 结果:
  17. "http-8081-11" daemon prio=10 tid=0x00002aab049a1800 nid=0x52bb in Object.wait() [0x0000000042c75000]
  18. java.lang.Thread.State: WAITING (on object monitor)
  19. at java.lang.Object.wait(Native Method)
  20. at java.lang.Object.wait(Object.java:485)
  21. at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)
  22. 在结果中查找52f1,可看到当前线程在做什么。

以上结果查询慢的可以使用github上的脚本快速查看:
show-busy-java-threads

参考