Common ways to get Java thread dumps

1. Introduction to thread dump

A thread dump (Thread Dump) is a snapshot of all thread state information in the JVM.

Thread dumps generally use text format, which can be saved to a text file, and then manually viewed and analyzed, or automatically analyzed using tools/APIs.

The threading model in Java directly uses the thread scheduling model of the operating system, and only performs simple encapsulation.

Thread call stack, also known as method call stack. For example, in the process of program execution, there is a series of method call chains: obj1.method2called obj2.methodB, obj2.methodBand called again obj3.methodC. The state of each thread can be represented by this call stack.

Thread dumps show the behavior of individual threads and are very useful for diagnosing and troubleshooting problems.

Below we use specific examples to demonstrate various tools for obtaining Java thread dumps and how to use them.

2. Use the tools that come with the JDK

We generally use the command line tools that come with the JDK to obtain thread dumps of Java applications. These tools are in the bin folder of the JDK main directory.

So, just configure the PATH path. If you don’t know how to configure it, you can refer to: JDK environment preparation

2.1 jstack tool

jstack is a built-in command line tool of JDK, which is specially used to view thread status, and can also be used to perform thread dump.

Generally, first find the pid corresponding to the Java process through the jpsor pscommand, and then output the thread dump through the pid in the console. Of course, we can also redirect the output to a file.

The basic parameter format for obtaining thread dumps using the jstack tool is:

jstack [-F] [-l] [-m] <pid>

Please see the specific demonstration below:

# 1. 查看帮助信息
jstack -help

The output is something like:

Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

The corresponding parameter options are optional. The specific meaning is as follows:

  • -Foption, to enforce thread dump; sometimes jstack pidit will freeze, you can add -Fthe flag
  • -loption, will look for synchronizers and resource locks owned in heap memory
  • -moption, additionally print native stack frames (C and C++)

For example, to take a thread dump and output the result to a file:

jstack -F 17264 > /tmp/threaddump.txt

Use jpsthe command to get the pid of the local Java process.

2.2 Java Mission Control

Java Mission Control (JMC) is a client graphical interface tool for collecting and analyzing various data of Java applications.
When JMC is started, it first displays a list of Java processes running on the local computer. Of course, you can also connect to a remote Java process through JMC.

You can right-click the corresponding process and select "Start Flight Recording (start flight recording)". After finishing, the "Threads" tab shows a "thread dump":

insert image description here

2.3 jvisualvm

jvisualvm is a client graphical interface tool, which is simple and practical, and can be used to monitor Java applications, troubleshoot and perform performance analysis on JVM.

Can also be used to obtain thread dumps. Right-click on the Java process, select the "Thread Dump" option, and you can create a thread dump, which will automatically open in a new tab when complete:

insert image description here

2.4 jcmd

The jcmd tool essentially sends a string of commands to the target JVM. Although many features are supported, it does not support connecting to remote JVMs - it can only be used on the local machine of the Java process.

One of the commands is Thread.printto take a thread dump, an example usage is as follows:

jcmd 17264 Thread.print

2.5 console

The jconsole tool can also view thread stack traces.
Open jconsole and connect to the running Java process, navigate to the "Threads" tab, you can view the stack trace of each thread:

insert image description here

2.6 Summary

It turns out that thread dumps can be obtained using a lot of tools in the JDK. Let's recap, and summarize their pros and cons:

  • jstack: The easiest and most convenient tool to obtain thread dumps; after Java 8, the jcmd tool can be used instead;
  • jmc: Enhanced JDK performance analysis and problem diagnosis tool. The overhead of profiling with this tool is very low.
  • jvisualvm: A lightweight open source analysis tool with a great graphical interface and supports various powerful functional plug-ins.
  • jcmd: Very powerful native tool, supports Java 8 and above. Integrate the functions of multiple tools, such as: capture thread dump (jstack), heap dump (jmap), view system properties and view command line parameters (jinfo)
  • jconsole: Can also be used to view thread stack trace information.

3. Using Linux commands

In enterprise application servers, only JRE may be installed for security reasons. At this time, these JDK built-in tools cannot be used.
But there is still a way to get a thread dump.

3.1 Using kill -3the command

In systems such as Unix/Linux, you can use killthe command to obtain thread dumps, and the underlying implementation principle is to kill()send signal parameters to the process through system calls. What needs to be sent here is -3the signal.

jpsGenerally, first find the pid corresponding to the JAVA process through , kill -3the usage example is as follows:

kill -3 17264

3.2 Ctrl + Break (Windows)

In the command line window of the Windows operating system, you can use the key combination Ctrl + Breakto obtain a thread dump. Of course, one needs to first navigate to the console window where the Java program was launched, and then press CTRLthe key and Breakkey at the same time.

It should be noted that some keyboards do not have a " Break" key.
In this case, CTRL, SHIFT, and Pausekeys can be used in combination.

Both commands can print a thread dump to the console.

4. Use ThreadMxBean programmatically

JMX technology supports a variety of fancy operations. A thread dump can ThreadMxBeanbe performed with .

The sample code is as follows:

private static String threadDump(boolean lockedMonitors, boolean lockedSynchronizers) {
    
    
    StringBuffer threadDump = new StringBuffer(System.lineSeparator());
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    for(ThreadInfo threadInfo : threadMXBean.dumpAllThreads(lockedMonitors, lockedSynchronizers)) {
    
    
        threadDump.append(threadInfo.toString());
    }
    return threadDump.toString();
}

What the above code does is very simple, first ManagementFactoryget ThreadMxBeanthe object through .
The method's boolean parameters lockedMonitorsand lockedSynchronizers, indicate whether to export the held synchronizer and monitor locks.

However, this approach has some drawbacks:

    1. The performance is not very good and consumes a lot of resources.
    1. threadDump.toString()The method will only output up to 8 stack frames ( MAX_FRAMES = 8); you can copy the toString code and modify/filter it yourself.
    1. Native threads (such as GC threads) will not be dumped.

alternative plan:

    1. Call jstack through Runtime to get thread dump information; if it fails, it will fall back to JMX mode;

Part of the code:


    public static String jStackThreadDump() {
    
    
        // 获取当前JVM进程的pid
        long currentPid = currentPid();
        // 组装命令
        String cmdarray[] = {
    
    
                "jstack",
                "" + currentPid
        };
        ProcessBuilder builder = new ProcessBuilder(cmdarray);
        String threadDump = "";
        try {
    
    
            Process p = builder.start();
            final BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            StringJoiner sj = new StringJoiner(System.lineSeparator());
            reader.lines().iterator().forEachRemaining(sj::add);
            threadDump = sj.toString();
            p.waitFor();
            p.destroy();
        } catch (Throwable e) {
    
    
            e.printStackTrace();
        }
        return threadDump;
    }

    public static long currentPid() {
    
    
        final long fallback = -1;
        final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
        final int index = jvmName.indexOf("@");
        if (index < 1) {
    
    
            return fallback;
        }
        String pid = jvmName.substring(0, index);
        if (null != pid && pid.matches("\\d+")) {
    
    
            return Long.parseLong(pid);
        }
        return fallback;
    }

5. Summary

We have shown various methods of obtaining thread dumps with concrete examples.

It first introduces various JDK built-in tools,
then discusses the command line method, and
finally introduces the method of JMX programming.

For complete sample code, please refer to the GitHub repository .

6. Appendix: Thread status and sample code

Thread status can be referred to Thread.State, including:

  • NEW: Not started; For example, the start method has not yet been executed (finished);
  • RUNNABLE: Runnable status; this is the perspective of the JVM, and whether the CPU is being used depends on the scheduling of the operating system;
  • BLOCKED: Blocked state; such as entering a synchronization method/synchronization block, waiting for a lock resource;
  • WAITING: Waiting for lock resources, such as Unsafe.park(), Object.wait()etc.
  • TIMED_WAITING: Time-limited waiting for lock resources, such as Unsafe.park(), Object.wait()etc.
  • TERMINATED: Terminated; the thread's task has finished executing.

Test code:


import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 简单模拟线程的各种状态
public class ThreadStateTest implements Runnable {
    
    
    public final Lock lock = new ReentrantLock(true);
    public final CountDownLatch beforeMonitorLatch = new CountDownLatch(1);
    public final CountDownLatch beforeLockLatch = new CountDownLatch(1);
    public final CountDownLatch toSleepLatch = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {
    
    
        // Runnable task
        ThreadStateTest task = new ThreadStateTest();
        // 新创建线程对象
        Thread thread = new Thread(task);
        // 1. 线程未开始; NEW 状态;
        System.out.println("1. before start: thread.getState(): " + thread.getState());
        assertEquals(Thread.State.NEW, thread.getState());
        // 把重量锁抢了
        synchronized (task) {
    
    
            // 启动线程;
            thread.start();
            // 等待执行到要请求管程锁
            task.beforeMonitorLatch.await();
            TimeUnit.MILLISECONDS.sleep(100L);
            // 3. 线程在阻塞状态: 等待管程锁
            System.out.println("3. blocked by monitor: thread.getState(): " + thread.getState());
            assertEquals(Thread.State.BLOCKED, thread.getState());
            // 将轻量锁抢了
            task.lock.lock();
        }
        // 等待执行到要请求锁
        task.beforeLockLatch.await();
        // 稍微等一等
        TimeUnit.MILLISECONDS.sleep(100L);
        // 4. 等待状态; 此处是等待轻量锁;
        System.out.println("4. waiting lock: thread.getState(): " + thread.getState());
        assertEquals(Thread.State.WAITING, thread.getState());
        // 释放锁
        task.lock.unlock();
        // 让线程继续执行
        task.toSleepLatch.countDown();
        TimeUnit.MILLISECONDS.sleep(100);
        // 此时 thread 应该在睡眠中
        System.out.println("5.Thread in sleep: thread.getState(): " + thread.getState());
        assertEquals(Thread.State.TIMED_WAITING, thread.getState());
        // 等线程结束来汇合
        thread.join();
        System.out.println("6. after join: thread.getState(): " + thread.getState());
        assertEquals(Thread.State.TERMINATED, thread.getState());
    }

    @Override
    public void run() {
    
    
        System.out.println("=== enter run() ===");
        // 获取执行此任务的线程;
        Thread thread = Thread.currentThread();
        // 2. 线程在执行过程中; 在JVM看来属于可执行状态
        assertEquals(Thread.State.RUNNABLE, thread.getState());
        System.out.println("2. executing run: thread.getState(): " + thread.getState());

        //请求管程锁
        System.out.println("=== before synchronized (this)===");
        beforeMonitorLatch.countDown();
        synchronized (this) {
    
    
            System.out.println("===synchronized (this) enter===");
        }

        // 设置标识: 即将请求轻量锁
        beforeLockLatch.countDown();
        System.out.println("===before lock.lock()===");
        // 等待锁
        lock.lock();
        lock.unlock();

        try {
    
    
            // 等待标志: 需要睡眠
            this.toSleepLatch.await();
            // 睡眠500毫秒
            System.out.println("===before sleep()===");
            TimeUnit.MILLISECONDS.sleep(500L);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("===finish run()===");
    }

    // 工具方法; 程序断言相等
    static public void assertEquals(Object expected, Object actual) {
    
    
        if (false == Objects.equals(expected, actual)) {
    
    
            throw new RuntimeException("Not Equals: expected=" + expected + "; actual=" + actual);
        }
    }
}

The execution result of the console output is:

1. before start: thread.getState(): NEW
=== enter run() ===
2. executing run: thread.getState(): RUNNABLE
=== before synchronized (this)===
3. blocked by monitor: thread.getState(): BLOCKED
===synchronized (this) enter===
===before lock.lock()===
4. waiting lock: thread.getState(): WAITING
===before sleep()===
5.Thread in sleep: thread.getState(): TIMED_WAITING
===finish run()===
6. after join: thread.getState(): TERMINATED

Related Links:

For more information refer to:

Guess you like

Origin blog.csdn.net/renfufei/article/details/112339222