jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。
线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情或者等待着什么资源。
jstack命令格式
jstack [option] vmid
option选项如下:
选项 | 作用 |
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于锁的附加信息 |
-m | 如果调用到本地方法的话,可以显示C/C++的堆栈 |
可以不用选项直接使用jstack pid
查看堆栈dump。
jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,应该如何找?
已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔段时间再执行一次stack命令获取thread dump,区分两份dump是否有差别。
通过thread dump分析线程状态
大多数情况下会基于thead dump分析当前各个线程的运行情况,如是否存在死锁、是否存在一个线程长时间持有锁不放等等。
在dump中,线程一般存在如下几种状态:
- RUNNABLE,线程处于执行中
- BLOCKED,线程被阻塞
- WAITING,线程正在等待
测试代码如下:
public class JstackCase { public static Executor executor = Executors.newFixedThreadPool(5); public static Object lock = new Object(); public static void main(String[] args){ Task task1=new Task(); Task task2=new Task(); executor.execute(task1); executor.execute(task2); } //如果不用static修饰,则必须如 Task task1=new JstackCase().new Task(); static class Task implements Runnable{ @Override public void run() { synchronized (lock){ calculate(); } } public void calculate(){ int i=0; while (true){ i++; } } } }
多线程竞争synchronized锁
很明显:线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态
locked <0x00000000d5fbd698>说明线程1对地址为<0x00000000d5fbd698>对象进行了加锁;
waiting to lock <0x00000000d5fbd698>说明线程2在等待地址为<0x00000000d5fbd698>对象上的锁;
通过wait挂起线程
线程1和2都处于WAITING状态:
线程1和2都是先locked <0x00000000d5fbd6c8>,再waiting on <0x00000000d5fbd6c8>。之所以先锁再等同一个对象,是因为wait方法需要先通过synchronized获得该地址对象的monitor;
waiting on <0x00000000d5fbd6c8>说明线程执行了wait方法之后,释放了monitor,进入到"Wait Set"队列,等待其它线程执行地址为 <0x00000000d5fbd6c8>对象的notify方法,并唤醒自己。
线程pid如何获取
线程pid如何获取
Linux下可以使用top命令,Windows下可以使用任务管理器查看。