Java Concurrent (1) —— jstack 查看jvm线程状态

使用场景

  1. 针对活着的进程做本地的或远程的线程dump。
  2. 针对core文件做线程dump。

jstack用于生成java虚拟机当前时刻的线程快照。

线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

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

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

1. CPU飙高,load高,响应慢

  1. 一个请求过程中多次dump;
  2. 对比多次dump文件的runnable线程。如果在一直在执行同一个方法,说明有问题

2. 查找CPU占用最多的线程

  1. 使用命令 top -H -p pid查看占用CUP最高的线程,找到对应的线程的ID,对应thread dump信息中的线程nid,(一个是十进制,一个是十六进制)。
  2. 在thread dump中,根据top命令查找到的did,查找对应的线程的堆栈信息。

3. CPU使用率不高,但是响应很慢

  1. 进行dump,查看是否有很多thread卡在了i/o、数据库等地方,定位瓶颈。

4. 请求无法响应

  1. 多次dump,对比所有的runnable线程是否存在一直在执行相同的方法

线程状态

Java Concurrent (1) —— jstack 查看jvm线程状态_第1张图片
java线程生命周期

根据enumThread.State, java的Thread主要有以下几种状态

  • NEW - 线程初始化状态,在调用start()之前的状态。 不会出现在Dump[1]中。
  • RUNNABLE - 运行状态。在运行中状态,可能看到locked,表明它获得了某把锁。可分成下列两种状态
    • 就绪态 - 线程已经获得执行所需的资源,等待CPU分配执行权。所有就绪态的线程存放在就绪队列中。
    • 运行态 - 线程已经获得了CPU执行权,正在执行。(每个CPU在每个时刻只有一条运行态的线程)
  • BLOCKED - 受阻塞的状态,等待某个监视器锁。在阻塞队列中保存。处于阻塞队列中的线程会不断请求资源,一旦请求成功就进入就绪队列,等待执行。
  • WAITING - 线程调用了Object.wait(), Thread.join(), LockSupport.park()函数且无timeout参数时,线程进入等待状态。线程保存在等待队列中。等待线程需要其他线程的指示。进入等待状态的线程会释放CPU的执行权和资源(包括锁)。
  • TIMED_WAITING - 线程调用了Thread.sleep(), Object.wait(long), Thread.join(long), LockSupport.parkNanos(), LockSupport.parkUntil()函数时,线程进入超时等待状态。与WAITING的区别是,在等待一定时间后,会自动进入BLOCKED状态。
  • TERMINATED - 线程执行结束(包括正常结束和异常结束)后的状态。

Thread.State源码

public enum State {
    /**
    * Thread state for a thread which has not yet started.
    */
    NEW,
    
    /**
    * Thread state for a runnable thread.  A thread in the runnable
    * state is executing in the Java virtual machine but it may
    * be waiting for other resources from the operating system
    * such as processor.
    */
    RUNNABLE,

    /**
    * Thread state for a thread blocked waiting for a monitor lock.
    * A thread in the blocked state is waiting for a monitor lock
    * to enter a synchronized block/method or
    * reenter a synchronized block/method after calling
    * {@link Object#wait() Object.wait}.
    */
    BLOCKED,
    
    /**
    * Thread state for a waiting thread.
    * A thread is in the waiting state due to calling one of the
    * following methods:
    * 
    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

Monitor

Java主要使用Monitor实现线程之间的互斥与协作。每个Java对象有且仅有一个monitor。下图描述了线程和Monitor之间的关系,以及线程的状态转换。

[图片上传失败...(image-5032-1601690492151)]

  • Entry Set - 当线程请求进入临界区时,进入Entry Set队列,处于该队列的线程在Dump中显示waiting for monitor entry状态。请求后两种可能。
    • 该monitor不被其他线程拥有,Entry Set里也没有其他等待线程。该线程成为Owner,执行临界区代码。线程进入Runnable状态。
    • 该monitor被其他线程拥有,该线程在Entry Set 中继续等待。Dump会显示该线程处于waiting for monitor entry状态。
  • The Owner - 当前线程拥有锁。
  • Wait Set - 线程通过对象的wait()方法,释放了对象的锁,并在等待区等待被唤醒。在Dump中显示in Object.wait()

jstack 命令

基本命令

  1. jps -ml (获取PID)
  2. jstack [option]

jstack的基本参数:

  • -F 当’jstack [-l] pid’没有相应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack),一般情况不需要使用
  • -l 长列表。打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间)。-l 建议不要用。一般情况不需要使用
  • -m 打印java和native c/c++框架的所有栈信息。可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用。
  • -h | -help 打印帮助信息。

示例:

XIAOYUC-M-C2V9:test xiaoyuc$ jps -ml
43153 MultiThreadProgramming.Chapter3.p_c_allWait.Run
43154 org.jetbrains.jps.cmdline.Launcher /Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/platform-api.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/log4j.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/slf4j-api-1.7.25.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/maven-resolver-spi-1.3.3.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/oro-2.0.8.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/asm-all-7.0.1.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/jps-model.jar:/Users/xiaoyuc/Libr
26028 org.jetbrains.jps.cmdline.Launcher /Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/platform-api.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/log4j.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/slf4j-api-1.7.25.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/maven-resolver-spi-1.3.3.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/oro-2.0.8.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/asm-all-7.0.1.jar:/Users/xiaoyuc/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.7846.76/IntelliJ IDEA.app/Contents/lib/jps-model.jar:/Users/xiaoyuc/Libr
43229 
43261 sun.tools.jps.Jps -ml
623 
XIAOYUC-M-C2V9:test xiaoyuc$ jstack 43153
2020-09-18 11:53:28
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.241-b07 mixed mode):

"DestroyJavaVM" #17 prio=5 os_prio=31 tid=0x00007f89ea02c000 nid=0x1903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #16 daemon prio=9 os_prio=31 tid=0x00007f89ea812000 nid=0x5c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"produce 2" #12 prio=5 os_prio=31 tid=0x00007f89ed000000 nid=0xa403 in Object.wait() [0x000070000f12c000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ac2bc90> (a java.lang.String)
        at java.lang.Object.wait(Object.java:502)
        at MultiThreadProgramming.Chapter3.p_c_allWait.Product.setValue(Product.java:17)
        - locked <0x000000076ac2bc90> (a java.lang.String)
        at MultiThreadProgramming.Chapter3.p_c_allWait.ThreadP.run(ThreadP.java:13)

"produce 1" #11 prio=5 os_prio=31 tid=0x00007f89ec818000 nid=0x5a03 in Object.wait() [0x000070000f029000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ac2bc90> (a java.lang.String)
        at java.lang.Object.wait(Object.java:502)
        at MultiThreadProgramming.Chapter3.p_c_allWait.Product.setValue(Product.java:17)
        - locked <0x000000076ac2bc90> (a java.lang.String)
        at MultiThreadProgramming.Chapter3.p_c_allWait.ThreadP.run(ThreadP.java:13)

"customer 2" #14 prio=5 os_prio=31 tid=0x00007f89e8009800 nid=0x5803 in Object.wait() [0x000070000ef26000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ac2bc90> (a java.lang.String)
        at java.lang.Object.wait(Object.java:502)
        at MultiThreadProgramming.Chapter3.p_c_allWait.Customer.getValue(Customer.java:15)
        - locked <0x000000076ac2bc90> (a java.lang.String)
        at MultiThreadProgramming.Chapter3.p_c_allWait.ThreadC.run(ThreadC.java:13)

"customer 1" #13 prio=5 os_prio=31 tid=0x00007f89e98df000 nid=0xa603 in Object.wait() [0x000070000ee23000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ac2bc90> (a java.lang.String)
        at java.lang.Object.wait(Object.java:502)
        at MultiThreadProgramming.Chapter3.p_c_allWait.Customer.getValue(Customer.java:15)
        - locked <0x000000076ac2bc90> (a java.lang.String)
        at MultiThreadProgramming.Chapter3.p_c_allWait.ThreadC.run(ThreadC.java:13)

"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007f89e98de000 nid=0xa903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007f89ea801800 nid=0x5503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007f89e7001000 nid=0x3b03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007f89eb800000 nid=0x3c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007f89e98ab000 nid=0x3e03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007f89e903f800 nid=0x3f03 runnable [0x000070000e70e000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x000000076ac83750> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x000000076ac83750> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007f89e8021800 nid=0x3603 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007f89ea003000 nid=0x4703 in Object.wait() [0x000070000e508000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007f89e801a800 nid=0x3103 in Object.wait() [0x000070000e405000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007f89eb004000 nid=0x3003 runnable 

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007f89e980e800 nid=0x1f07 runnable 

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007f89e7800800 nid=0x2003 runnable 

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007f89e7000000 nid=0x5403 runnable 

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007f89e9001800 nid=0x2b03 runnable 

"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007f89e7002800 nid=0x5103 runnable 

"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007f89ea001000 nid=0x4f03 runnable 

"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007f89ea800000 nid=0x4d03 runnable 

"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007f89ea801000 nid=0x4b03 runnable 

"GC task thread#8 (ParallelGC)" os_prio=31 tid=0x00007f89e7003000 nid=0x2d03 runnable 

"GC task thread#9 (ParallelGC)" os_prio=31 tid=0x00007f89e9002000 nid=0x2f03 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007f89eb801000 nid=0xa703 waiting on condition 

JNI global references: 317

Dump分析

Dump中的关键状态

1. Waiting on condition

等待资源,或等待某个条件的发生。可能原因如下:

  • 如果堆栈信息显示是应用代码,证明该线程在等待资源。一般是大量读取某资源,且该资源采用了资源锁的情况下,线程进入等待状态。
  • 正在等待其他线程的执行。
  • 如果发现大量线程在Waiting on condition状态,从线程stack看,正等待网络读写,这可能是一个网络瓶颈。
    • 一种情况是网络非常忙,网络带宽几乎消耗完了,仍有大量数据等待网络读写。
    • 另一种是网络空闲,但是由于路由等问题,导致包无法正常到达。
  • 线程正在sleep。

2. Waiting for Monitor Entry 和 in Object.wait()

详见Monitor章节

3. Deadlock

线程死锁,一般指多个线程调用间,进入相互资源占用,导致一直等待无法释放的情况。

4. Blocked

线程长时间未获得资源,被容器的线程管理器标识为阻塞状态。

参考资料

  • Java运行状态分析1: 线程及线程状态
  • Java运行状态分析2: 获取线程状态及堆栈信息
  • Java命令——jstack工具
  • jstack查看jvm线程状态
  • Java常见命令及Java Dump介绍
  • 啃碎并发(四):Java线程Dump分析
  • Java线程Dump的分析——jstack pid
  • Java多线程(二)——Monitor

  1. Java虚拟机的运行时快照。将Java虚拟机运行时的状态和信息保存到文件。堆Dump,包含线程Dump,幵包含所有堆对象的状态。二进制格式。线程Dump,包含所有线程的运行状态。纯文本格式。线程栈是瞬时记录,一般都需要结合程序的日志进行跟踪问题。 ↩

你可能感兴趣的:(Java Concurrent (1) —— jstack 查看jvm线程状态)