jstack命令详解【转】

jstack命令详解 

 

简介

  1. jstack命令用于打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息[1]
  2. jstack命令可以生成JVM当前时刻的线程快照。线程快照是当前JVM内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
  3. 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
  4. 当指定的进程在64位Java虚拟机上运行时,可能需要指定-J-d64选项,例如:jstack -J-d64 -m pid。
  5. 该命令可能在未来的版本中不可用!!!

语法

jstack [ options ] pid

pid:Java进程的ID,可以通过jps命令查询到。

jstack [ options ] executable core

executable: 产生core dump的Java可执行程序

core:要打印的堆栈跟踪的核心文件

jstack [ options ] [ server-id@ ] remote-hostname-or-IP

server-id:当多个DEBUG服务器在同一远程主机上运行时,可使用的可选唯一ID。

remote-hostname-or-IP:远程DEBUG的服务器主机名或IP地址

options 参数说明

-F

当 jstack [-l] pid 没有响应时,强制打印一个堆栈转储。

-l

打印关于锁的其他信息,比如拥有的java.util.concurrent ownable同步器的列表。

-m

打印包含Java和本机C/ C++帧的混合模式堆栈跟踪。

-h

打印帮助信息

-help

打印帮助信息

例子

jstack pid
jstack -F pid
jstack -l pid
jstack -m pid
jstack -h pid
jstack -H pid

日志文件分析

  1. 可以通过 jstack [options] pid >> /xxx/xx/x/dump.log命令,将堆栈信息输出到dump.log文件后,然后下载到本地排查文件。
  2. 在dump.log日志文件里,需要重点关注的线程状态
Deadlock(死锁)

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

1、死锁代码如下

package com.example;

public class Test {

    public static void main(String[] args) {
        Thread a = new MyThreadA();
        Thread b = new MyThreadB();
        a.setName("线程A");
        a.start();
        b.setName("线程B");
        b.start();

    }

}

class MyThreadA extends Thread {
    @Override
    public void run() {
        System.out.println("================B===================");
        synchronized (A.A) {
            System.out.println("线程【" + Thread.currentThread().getName() + "】开始执行");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (B.B) {
            }
            System.out.println("线程【" + Thread.currentThread().getName() + "】执行结束。B.B = " + B.B + "; A.A = " + A.A);
        }
    }
}

class MyThreadB extends Thread {
    @Override
    public void run() {
        System.out.println("================B===================");
        synchronized (B.B) {
            System.out.println("线程【" + Thread.currentThread().getName() + "】开始执行");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (A.A) {
            }
            System.out.println("线程【" + Thread.currentThread().getName() + "】执行结束。B.B = " + B.B + "; A.A = " + A.A);
        }
    }
}

class A {
    static Integer A = new Integer(1);
}

class B {
    static Integer B = new Integer(1);
}

2、运行程序后,通过jstack -F pid >> /xxx/xx/x/a.log 将堆栈信息输出到a.log中

Attaching to process ID 9356, please wait...
Attaching to process ID 18972, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11
Deadlock Detection:

Found one Java-level deadlock:
=============================

"线程A":
  waiting to lock Monitor@0x0000000018593468 (Object@0x00000000d66d0608, a java/lang/Integer),
  which is held by "线程B"
"线程B":
  waiting to lock Monitor@0x0000000018590c88 (Object@0x00000000d662c8f0, a java/lang/Integer),
  which is held by "线程A"

Found a total of 1 deadlock.

Thread 4: (state = BLOCKED)


Thread 28: (state = BLOCKED)
 - com.example.MyThreadB.run() @bci=69, line=47 (Interpreted frame)


Thread 27: (state = BLOCKED)
 - com.example.MyThreadA.run() @bci=69, line=29 (Interpreted frame)


Thread 17: (state = BLOCKED)


Thread 16: (state = BLOCKED)


Thread 15: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=144 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove() @bci=2, line=165 (Interpreted frame)
 - java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=216 (Interpreted frame)


Thread 14: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Object.wait() @bci=2, line=502 (Interpreted frame)
 - java.lang.ref.Reference.tryHandlePending(boolean) @bci=54, line=191 (Interpreted frame)
 - java.lang.ref.Reference$ReferenceHandler.run() @bci=1, line=153 (Interpreted frame)


3、日志分析

Found one Java-level deadlock说明发现了一个死锁。

waiting to lock Monitor@0x0000000018593468 表示 线程A正在等待给Monitor@0x0000000018593468上锁,但是Monitor@0x0000000018593468被线程B持有。

waiting to lock Monitor@0x0000000018590c88 表示线程B正在等待给Monitor@0x0000000018590c88上锁,但是Monitor@0x0000000018590c88被线程A持有。

从上图可以知道,线程A正在等待锁,但是这个锁被线程B持有;而线程B也在等待锁,但是这个锁被线程A持有。线程A和现场B只能一直等待下去,造成了死锁。

state = BLOCKED 表明线程的状态是阻塞的。

通过com.example.MyThreadB.run() @bci=69, line=47 (Interpreted frame) com.example.MyThreadA.run() @bci=69, line=29 (Interpreted frame)定位到了出现死锁的具体的类和行数。

Waiting on condition(等待资源)

该状态出现在线程等待某个条件的发生。最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态。而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。

如果发现有大量的线程都在处在 Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行,原因有两种:一种情况是网络非常忙,几 乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。

另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。

代码和日志分析参见下文【Blocked(阻塞)】部分的分析。

Waiting on monitor entry(等待获取监视器)

Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。

代码和日志分析参见下文【Blocked(阻塞)】部分的分析。

Blocked(阻塞)

线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。

1、代码

public class Test {

    public static void main(String[] args) throws InterruptedException {
        final Thread myThread = new Thread() {
            @SneakyThrows
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println(Thread.currentThread());
                    TimeUnit.SECONDS.sleep(60);
                }
            }
        }; // 到这一步,线程的状态是NEW
        // 给线程起个名字,方便排查问题
        myThread.setName("测试线程");
        // 到这一步,线程的状态是RUNNABLE
        myThread.start();

        synchronized (myThread) {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(60);
        }
    }

}

2、运行程序后,通过jstack -l pid >> /xxx/xx/x/a.log 将堆栈信息输出到a.log中

2022-03-05 16:04:45
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"测试线程" #12 prio=5 os_prio=0 tid=0x000000001b27b000 nid=0x1d68 waiting for monitor entry [0x000000001bbcf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.example.Test$1.run(Test.java:15)
	- waiting to lock <0x00000000d6349040> (a com.example.Test$1)

   Locked ownable synchronizers:
	- None

"main" #1 prio=5 os_prio=0 tid=0x0000000003595800 nid=0x42d4 waiting on condition [0x000000000342f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.example.Test.main(Test.java:27)
	- locked <0x00000000d6349040> (a com.example.Test$1)

   Locked ownable synchronizers:
	- None
......

3、日志分析

  • 日志格式
    • "测试线程"代表线程的名称。在实际开发中,要取一个和业务相关、见名知意的名字,便于排查问题。
    • prio=5”表示线程的优先级。
    • os_prio=0”表示操作系统级别的优先级。
    • tid=0x000000001b27b000”表示线程id。
    • nid=0x1d68”表示操作系统映射的线程id。
    • waiting for monitor entry”表示线程正在获取锁。
    • “0x000000001bbcf000”表示线程栈的起始地址。
  • 具体分析

程序运行后,输出信息如下图:

从程序输出信息可以看出,main线程首先获取到了"测试线程"对象上的锁,然后执行sleep操作,main线程的状态变为TIMED_WAINTING,而"测试线程" 并没有获取到这个锁,因此进入BLOCKED状态。

“测试线程”正在waiting to lock <0x00000000d6349040>,即试图在地址为0x00000000d6349040所在的对象获取锁;而该对象却被main线程加了锁(locked <0x00000000d6349040>)。

“main”线程正在waiting on condition [0x000000000342f000],说明正在等待某个条件触发。而“测试线程”waiting for monitor entry [0x000000001bbcf000],说明该线程正在等待获取锁。

转自

jstack命令详解 - 门罗的魔术师 - 博客园
https://www.cnblogs.com/powerwu/articles/16363552.html

posted @   paul_hch  阅读(324)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示