Thread Dump介绍Thread Dump是非常有用的诊断JAVA应用问题的工具 。每一个Java虚拟机都有及时生成所有线程在某一点状态的thread-dump的能力,虽然各个 Java虚拟机打印的thread dump略有不同,但是大多都提供了每个线程的所有信息,例如: 线程状态、线程 Id、本机 Id、线程名称、堆栈跟踪、优先级 。
Thread Dump特点
- 能在各种操作系统下使用
- 能在各种Java应用服务器下使用
- 可以在生产环境下使用而不影响系统的性能
- 可以将问题直接定位到应用程序的代码行上(对于线上排查问题非常有用)
如何抓取Thread Dump一般当服务器挂起,崩溃或者性能底下时,就需要抓取服务器的线程堆栈(Thread Dump)用于后续的分析. 在实际运行中,往往一次 dump的信息,还不足以确认问题 。为了反映线程状态的动态变化,需要接连多次做threaddump,每次间隔10-20s,建议至少产生三次 dump信息,如果每次 dump都指向同一个问题,我们才确定问题的典型性 。
获取thread dump
JDK自带命令行工具获取PID,再获取ThreadDump:1. jps 或 ps –ef|grep java (获取PID)2. jstack [-l ]<pid> | tee -a jstack.log(获取ThreadDump)
实操演练获取所有线程的thread dump 分两步.- 第一步 获取进程的PID
使用Jps 获取所有java进程的信息
文章插图
- 第二步 选取对应的pid 例如上图红框中的数字串 使用Jstack获取所有线程栈信息
jstack-l9468 | tee -a jstack.log
文章插图
日志字段分析我们把Thread dump文件分为2个部分来理解
拿我们的例子来说:
//头部信息包含 当前时间jvm信息2021-01-14 17:00:51Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):
//线程info信息块"ajp-nio-8019-exec-7" #75 daemon prio=5 os_prio=0 tid=0x00007fa0cc37e800 nid=0x2af3 waiting on condition [0x00007fa02eceb000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for<0x00000000f183aa30> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)at org.Apache.Tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at org.apache.tomcat.util.threads.TaskThread$WrAppingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:748)
线程info信息块各个参数的意义:- 线程名称:ajp-nio-8019-exec-7
- 线程类型:daemon
- 优先级: 默认是5
- jvm线程id:tid=0x00007fa0cc37e800,jvm内部线程的唯一标识(通过java.lang.Thread.getId()获取,通常用自增方式实现 。)
- 对应系统线程id(NativeThread ID):nid=0x2af3,和top命令查看的线程pid对应,不过一个是10进制,一个是16进制 。(通过命令:top -H -p pid,可以查看该进程的所有线程信息)
- 线程状态:java.lang.Thread.State: WAITING (parking)
- 线程调用栈信息:用于代码的分析 。堆栈信息应该从下向上解读,因为程序调用的顺序是从下向上的 。
- deadlock死锁线程,一般指多个线程调用期间进入了相互资源占用,导致一直等待无法释放的情况 。
- runnable一般指该线程正在执行状态中,该线程占用了资源,正在处理某个操作,如通过SQL语句查询数据库、对某个文件进行写入等 。
- blocked线程正处于阻塞状态,指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程 。
- waiting on condition线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析 。
推荐阅读
- ThreadLocal原理及使用场景大揭秘
- JVM生成的3种文件,你都见过吗?
- 建议收藏 一文深度讲解JVM 内存分析工具 MAT及实践
- Java堆和栈的区别和介绍以及JVM的堆和栈
- java安全编码指南之:Thread API调用规则
- JVM常见线上问题 → CPU 100%、内存泄露 问题排查
- Java性能调优:JVM性能监控常用方法
- 一次完整的JVM堆外内存泄漏故障排查记录
- 使用微软的 ProcDump 调试 Linux 进程
- 什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?