时间:2022-10-16 11:23:00 | 栏目:JAVA代码 | 点击:次
jstack - Prints Java thread stack traces for a Java process, core file, or remote debug server.
Jstack 主要的作用是生成当前进程中所有线程的信息,也就是当前时刻 JVM 的线程快照,通过线程的信息我们可以定位到程序中出现长时间停顿、CPU 占用率过高等问题。
线程快照中的信息是当前 java 虚拟机内每一条线程正在执行的方法的堆栈集合,有了堆栈信息我们就可以分析出我们的程序问题出现在那,比如线程间死锁、外部资源请求时间过长、死循环等。
使用:
jstack [ options ] pid jstack [ options ] executable core jstack [ options ] [ server-id@ ] remote-hostname-or-IP OPTIONS -F Force a stack dump when jstack [-l] pid does not respond. -l Long listing. Prints additional information about locks such as a list of owned java.util.concurrent ownable synchronizers. See the AbstractOwnableSynchronizer class description at http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/AbstractOwnableSynchronizer.html -m Prints a mixed mode stack trace that has both Java and native C/C++ frames.
以一个发生死锁的例子来看一下使用 Jstack 查看到的信息
public class Jstack { private static Object obj1 = new Object(); private static Object obj2 = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized (obj1) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { } } }).start(); new Thread(() -> { synchronized (obj2) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1) { } } }).start(); } }
上面代码中,第一个线程拿到 obj1 的锁,等待 obj2 的锁,第二个线程拿到 obj2 的锁,等待 obj1 的锁,这样就会发生死锁。
先通过jps命令获取到先拿到当前的进程 pid,然后通过 jstack 获取线程的信息。可以看到有两个线程都处于阻塞状态。
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007fdff871c800 nid=0x3cc2 waiting for monitor entry [0x00007fdfce0fc000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.demo.jstack.Jstack.lambda$main$1(Jstack.java:36) - waiting to lock <0x000000076e925a90> (a java.lang.Object) - locked <0x000000076e925aa0> (a java.lang.Object) at com.example.demo.jstack.Jstack$$Lambda$2/2052001577.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Thread-0" #11 prio=5 os_prio=0 tid=0x00007fdff871a800 nid=0x3cc1 waiting for monitor entry [0x00007fdfce1fc000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.demo.jstack.Jstack.lambda$main$0(Jstack.java:25) - waiting to lock <0x000000076e925aa0> (a java.lang.Object) - locked <0x000000076e925a90> (a java.lang.Object) at com.example.demo.jstack.Jstack$$Lambda$1/1174361318.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)
第一行显示可线程名、线程优先级、线程 id、线程状态描述等信息
第二行显示的是当前线程的状态
? Java 中线程的状态分为 NEW、RUNNABLE、BLOCKED、WATING、TIMED_WATING、TERMINATED,但是在快照中 NEW 状态是不会出现的。
再下面的就是当前线程的调用栈的信息。调用栈中包含了锁的信息。
? locked 表示使用 synchronized 申请对象锁成功,监视器的拥有者
? waiting to lock 表示使用 synchronized 申请对象锁未成功,进入等待区。
? waiting on 表示用 synchronized 申请对象锁成功后,调用了 wait 方法,进入对象的等待区等待。
? parking to wait for park 是基本的线程阻塞原语,不通过监视器在对象上阻塞。随 concurrent 包会出现的新的机制,与 synchronized 体系不同。
在最后也显示出了代码中出现死锁的信息
Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x00007fdfac006638 (object 0x000000076e925a90, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x00007fdfac003da8 (object 0x000000076e925aa0, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at com.example.demo.jstack.Jstack.lambda$main$1(Jstack.java:36) - waiting to lock <0x000000076e925a90> (a java.lang.Object) - locked <0x000000076e925aa0> (a java.lang.Object) at com.example.demo.jstack.Jstack$$Lambda$2/2052001577.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Thread-0": at com.example.demo.jstack.Jstack.lambda$main$0(Jstack.java:25) - waiting to lock <0x000000076e925aa0> (a java.lang.Object) - locked <0x000000076e925a90> (a java.lang.Object) at com.example.demo.jstack.Jstack$$Lambda$1/1174361318.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
好了,熟悉了 Jstack,我们用一段死循环的代码,通过 Jstack 来定位到使 CPU 占用 100%的代码行
public class JstackDemo { public static Executor executor = Executors.newFixedThreadPool(3); private 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); } public static class Task implements Runnable { @Override public void run() { synchronized (lock) { run0(); } } private void run0() { int i = 0; while (true) { i++; } } } }
1、首先通过top查看到使 CPU 占用到 100%的进程 id
2、使用top -Hp 进程id
查看占用 CPU 最多的线程 id
3、将线程 id 转换为 16 进制
17997 -> 464d
4、使用 Jstack 查看 Java 所在的进程,并找到相应的线程
总结