美文网首页
使用jstack分析Java线程

使用jstack分析Java线程

作者: 我不懂我不懂a | 来源:发表于2024-02-28 16:53 被阅读0次

    You use the jstack command to print Java stack traces of Java threads for a specified Java process. This command is experimental and unsupported.

    官方文档中,jstack是用于打印指定Java进程的线程堆栈跟踪,我们通常用jstack来分析死锁和死循环等场景。

    使用方式及参数

    Usage:
        jstack [-l][-e] <pid>
            (to connect to running process)
    
    Options:
        -l  long listing. Prints additional information about locks
        -e  extended listing. Prints additional information about threads
        -? -h --help -help to print this help message
    
    

    使用方式是找到Java进程id——pid,jstack pid就能打印出堆栈信息。-l参数可以打印锁的信息,-e可以打印线程的额外信息。

    jstack分析死锁

    我们写一个死锁的demo DeadlockDemo.java并且运行:

    Object lock1 = new Object();  
    Object lock2 = new Object();  
      
    Thread t1 = new Thread(() -> {  
        synchronized (lock1) {  
            System.out.println("Thread 1 acquired lock1");  
            try {  
                sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            synchronized (lock2) {  
                System.out.println("Thread 1 acquired lock2");  
            }  
        }  
    });  
      
    Thread t2 = new Thread(() -> {  
        synchronized (lock2) {  
            System.out.println("Thread 2 acquired lock2");  
            try {  
                sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            synchronized (lock1) {  
                System.out.println("Thread 2 acquired lock1");  
            }  
        }  
    });  
      
    t1.start();  
    t2.start();  
      
    // 等待两个线程执行完毕  
    try {  
        t1.join();  
        t2.join();  
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }  
      
    System.out.println("Main thread finished");
    

    使用jps -l查看java程序的端口

    jps -l
    12406 org.jetbrains.jps.cmdline.Launcher
    2156 com.intellij.idea.Main
    17119 jdk.jcmd/sun.tools.jps.Jps
    
    

    命令行执行jstack -l -e 12406

    2024-02-15 15:30:11
    Full thread dump OpenJDK 64-Bit Server VM (17.0.9+9-Ubuntu-120.04 mixed mode, sharing):
    

    第一部分是虚拟机信息,SMR全称是Safe Memory Reclamation,即jvm安全分配的线程:

    Threads class SMR info:
    _java_thread_list=0x00007f102c001740, length=14, elements={
    0x00007f10c0013af0, 0x00007f10c01765a0, 0x00007f10c0177990, 0x00007f10c017e170,
    0x00007f10c017f530, 0x00007f10c0180950, 0x00007f10c0182310, 0x00007f10c0183850,
    0x00007f10c018ccc0, 0x00007f10c01985d0, 0x00007f10c01c53c0, 0x00007f10c04d9610,
    0x00007f10c04e48d0, 0x00007f102c000d20
    }
    

    第二部分是线程堆栈信息,堆栈信息里第一行是线程的元信息:

    "main" #1 prio=5 os_prio=0 cpu=338.57ms elapsed=91.48s allocated=14953K defined_classes=1526 tid=0x00007f10c0013af0 nid=0x2a95 in Object.wait()  [0x00007f10c7d34000]
    
    • "main"线程名字
    • #1线程序号
    • prio优先级
    • os_prioos线程优先级
    • cpu线程获得cpu的时间
    • elapsed线程启动后经过的wall clock time
    • allocated分配的内存字节数
    • defined_classes线程定义的类个数
    • tid线程id
    • nidos线程id
    • in Object.wait()表示当前线程状态
    • [0x00007f10c7d34000]最新java堆栈指针sp
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(java.base@17.0.9/Native Method)
        - waiting on <0x000000071e77a5b0> (a java.lang.Thread)
        at java.lang.Thread.join(java.base@17.0.9/Thread.java:1313)
        - locked <0x000000071e77a5b0> (a java.lang.Thread)
        at java.lang.Thread.join(java.base@17.0.9/Thread.java:1381)
        at org.example.DeadlockDemo.main(DeadlockDemo.java:43)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.9/Native Method)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.9/NativeMethodAccessorImpl.java:77)
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.9/DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(java.base@17.0.9/Method.java:568)
        at com.sun.tools.javac.launcher.Main.execute(jdk.compiler@17.0.9/Main.java:419)
        at com.sun.tools.javac.launcher.Main.run(jdk.compiler@17.0.9/Main.java:192)
        at com.sun.tools.javac.launcher.Main.main(jdk.compiler@17.0.9/Main.java:132)
    

    后面跟着的是堆栈的详细信息,表示当前main线程状态是WAITING,表示正在执行Thread.join中,还在等待其他线程执行完。Locked ownable synchronizers:代表线程拥有的排它锁对象,例如ReentrantReadWriteLock.writeLock

    
    "VM Thread" os_prio=0 cpu=15.08ms elapsed=91.47s tid=0x00007f10c01724e0 nid=0x2a9b runnable  
    
    "GC Thread#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c007be30 nid=0x2a96 runnable  
    
    "G1 Main Marker" os_prio=0 cpu=0.13ms elapsed=91.48s tid=0x00007f10c008c3e0 nid=0x2a97 runnable  
    
    "G1 Conc#0" os_prio=0 cpu=0.08ms elapsed=91.48s tid=0x00007f10c008d350 nid=0x2a98 runnable  
    
    "G1 Refine#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c0144bc0 nid=0x2a99 runnable  
    
    "G1 Service" os_prio=0 cpu=16.20ms elapsed=91.48s tid=0x00007f10c0145ac0 nid=0x2a9a runnable  
    
    "VM Periodic Task Thread" os_prio=0 cpu=59.59ms elapsed=91.45s tid=0x00007f10c01c6d80 nid=0x2aa6 waiting on condition  
    
    JNI global refs: 9, weak refs: 0
    
    

    后面跟着的是jvm的线程信息。

    Found one Java-level deadlock:
    =============================
    "Thread-0":
      waiting to lock monitor 0x00007f1008001120 (object 0x000000071e77a520, a java.lang.Object),
      which is held by "Thread-1"
    
    "Thread-1":
      waiting to lock monitor 0x00007f0ffc000f60 (object 0x000000071e77a510, a java.lang.Object),
      which is held by "Thread-0"
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-0":
        at org.example.DeadlockDemo.lambda$main$0(DeadlockDemo.java:19)
        - waiting to lock <0x000000071e77a520> (a java.lang.Object)
        - locked <0x000000071e77a510> (a java.lang.Object)
        at org.example.DeadlockDemo$$Lambda$198/0x00007f1044140208.run(Unknown Source)
        at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)
    "Thread-1":
        at org.example.DeadlockDemo.lambda$main$1(DeadlockDemo.java:33)
        - waiting to lock <0x000000071e77a510> (a java.lang.Object)
        - locked <0x000000071e77a520> (a java.lang.Object)
        at org.example.DeadlockDemo$$Lambda$199/0x00007f1044140430.run(Unknown Source)
        at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)
    
    Found 1 deadlock.
    

    在堆栈信息的最后,jstack已经帮我们找到了死锁的原因,可以看到,thread-0锁住了<0x000000071e77a510>对象,正在等待<0x000000071e77a520>对象的释放,thread-1则相反。

    jstack分析死循环

    通过top找到占用大的java进程然后通过top -Hp pid找到占用异常的线程id21484,通过printf "%x\n"转换位16进制的pid,再去jstack输出找到对应堆栈执行位置进行分析jstack <pid> | grep -A 20 '<nid>'

    引用资料:
    [1] JDK14性能管理工具:jstack使用介绍
    [2] 原来jdk自带了这么好玩的工具 —— 使用 jstack定位死循环

    相关文章

      网友评论

          本文标题:使用jstack分析Java线程

          本文链接:https://www.haomeiwen.com/subject/aslfzdtx.html