A Java Virtual Machine (JVM) uses threads to execute all internal and external processes in Java. The use of threads allows the execution of different parts of an application simultaneously, whereas a Java thread bump contains the information about the threads. In this article, we will be discussing Java threads, Java thread dumps, and how to analyze them. We will also be looking into how it helps pinpoint the issues in an application and some of the analyzers you can use to troubleshoot your Java application.
What is a Java thread, and why is it used?
A thread is a single path followed when executing a process (program). A simple Java program can have just one thread, known as the main thread. It is created by JVM at the initial stage of execution when the main() method is invoked with the main thread. In the same way, multiple threads are created and are used in an attempt to perform multiple tasks at the same time.
Threads are created in Java by implementing an interface and extending the thread class. Every thread is created and controlled by the Java.lang.Thread class. A single-threaded application can handle only one task, while multi-threading can handle multiple tasks together. This is used to achieve parallelism by diving into a single process among multiple threads. This results in significantly better performance. All the threads within a process are dependent on each other. Therefore, they all share the same memory space.
What is meant by Java thread dump?
Java applications sometimes start to hang up or run slowly. This can be a nightmare for a Java developer, as identifying the root cause is not always easy. A thread dump provides all the information on the current state of a running Java process. After analyzing a Java thread dump, finding the actual cause of any problem or performance issue in an application can get relatively more straightforward. A Java thread dump contains all the threads in a stack, presented as a stack trace. All the information is written in plaintext, and it also allows users to save and share it with others. Information from a Java thread dump is mainly used for optimizing JVM and the application’s performance. It is also extensively used for diagnosing problems, such as deadlocks or thread contention.
All modern applications now involve multiple threading. With the increase in the usage of threads, it gets challenging to manage all of them, and multiple threads may not coordinate well with each other, resulting in a deadlock. In such a situation, Java thread dumps can be beneficial to inspect the state of threads to identify the root of the deadlock.
Generating a Java thread dump
We will not be going into detail on generating a Java thread dump. Just for a brief guide, the following are ways to generate a Java thread dump that you can explore further.
Using JDK tools
JDK offers various tools that can get the job done. Some of the prominent JDK tools include:
- Java Mission Control
Using ThreadMXBean interface
This management interface can also generate the Java thread dumps from the Java virtual machine.
public interface ThreadMXBean extends PlatformManagedObject
Using command-line interface
You can use these commands to capture the Java thread dump in specific applications where JDK is unavailable.
- “Control + Break” for Windows running systems.
- “Kill – 3” Command in systems running on Linux or Unix.
Reading a Java thread dump
Observe the following small extract from a Java thread dump:
2021-12-19 14:31:12 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.162-b12) "Atmosphere-Scheduler-3162" nid=137911 state=TIMED_WAITING  java.lang.Thread.State: TIMED_WAITING - waiting on <0x2db6a729> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) - locked <0x2db6a729> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at sun.misc.Unsafe.park(Native Method) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.poll(ScheduledThreadPoolExecutor.java:1129) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.poll(ScheduledThreadPoolExecutor.java:809) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) "http-nio-8080-exec-10" nid=1044 state=RUNNABLE  java.lang.Thread.State: RUNNABLE at sun.management.ThreadImpl.getThreadInfo1(Native Method) at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:176) at org.jahia.tools.jvm.ThreadMonitor.dumpThreadInfo(ThreadMonitor.java:352) at org.jahia.tools.jvm.ThreadMonitor.getFullThreadInfo(ThreadMonitor.java:500)
This may appear overwhelming at first, but it can be easily understood if you know what each part of a thread dump represents. The first line displays the timestamp of dump generation, while the second line shows the diagnostic information about the JVM. Following that, the thread dump lists down all the running threads and the stack trace dump of what each thread was executing when the dump was generated.
Each thread is represented in the following format:
“THREAD_NAME” nid=THREAD_ID state=THREAD_STATE [THREAD_LOCK_INFO] java.lang.Thread.State: [THREAD_STATE]
- THREAD_NAME : It represents the name of a thread. This can be useful if all the threads are given meaningful names. In the extract above, the thread’s name is “http-nio”.
- THREAD_ID: It is a unique thread identifier used in the Java Virtual Machine.
- THREAD_STATE: This indicates the current state of a thread. Following are the possible states of a thread
- NEW: A thread that has not yet started working.
- RUNNABLE: A running thread.
- BLOCKED: A thread that is blocked while waiting for a monitor lock.
- WAITING: A thread waiting indefinitely for another thread to first complete a task.
- TIMED_WAITING: A thread waiting for another thread to complete an action just for a specific time.
- TERMINATED: A thread that has ended.
- THREAD_LOCK_INFO: This value tells us whether the thread is holding a monitor lock or waiting to lock a specific monitor lock object. It comes in use to check for deadlocks or any long-held locks on a thread.
There are just two threads shown in this extract, and there can be hundreds or even thousands of simultaneous threads being executed. In such a case, the thread dump can be way lengthier and highly complicated to analyze for finding a cause of a problem.
To cater to that, you can use the following practices to analyze a Java thread dump.
Analyzing a Java thread dump
First, analyze your code
It is more suited to start the analysis with stack traces that contain your code. This section will also be more understandable as you can quickly identify which part of your code was executing when the dump was collected. This will be easier for you to identify if anything is abnormal in the execution.
If everything seems fine in your code, you should start looking at the other parts of the stack traces containing libraries, servers, and JDK code to understand the problem.
Focus on the relevant information
There is some information that you can skip in the thread dump. It will save you a lot of time and make the analyzing process easier. In the extract mentioned above, you can find a statement in the first thread:
The LockSupport.parkNanos method indicates that the thread is parked. A parked thread is disabled for scheduling until it gets a “permit” from a different thread.
It means you can skip such threads as they would not affect the performance of the process. Other than parked threads, threads with the waiting state can also be ignored for the same reason.
Resolving the blocked threads
Blocked threads are one of the primary reasons behind the degrading performance. It is very easy to identify from the status of a thread. Upon finding a blocked thread, you can try to extract the threads related to the locks that the blocked thread cannot obtain. Analyzing the stack trace from the thread currently holding the lock can help remove the block.
Resolving the deadlocked threads
After analyzing the thread dump, the detection and solution of deadlocked threads can be more accessible. As none of the threads can continue execution in a deadlock, the application stops working. If a single dreadlock is present, then the final section of the thread dump will print out the following information.
"Thread-2": waiting to lock monitor 0x00000250e4982480 (object 0x00000000894465b0, a java.lang.Object), which is held by "Thread-3" "Thread-3": waiting to lock monitor 0x00000250e4982380 (object 0x00000000894465a0, a java.lang.Object), which is held by "Thread-2" . . . "Thread-2": at DeadlockedProgram$DeadlockedRunnableImplementation.run(DeadlockedProgram.java:34) - waiting to lock <0x00000000894465b0> (a java.lang.Object) - locked <0x00000000894465a0> (a java.lang.Object) at java.lang.Thread.run(firstname.lastname@example.org/Thread.java:844) "Thread-3": at DeadlockedProgram $DeadlockRunnableImplementation.run(DeadlockedProgram.java:34) - waiting to lock <0x00000000894465a0> (a java.lang.Object) - locked <0x00000000894465b0> (a java.lang.Object) at java.lang.Thread.run(email@example.com/Thread.java:844)
This information is relatively easy to understand as the most part is in a human-readable format.
Online thread dump analyzers
If you still feel overwhelmed by data in thread dumps, numerous tools are available for analyzing thread dumps online on a browser. You have to copy the thread dump or upload the dump text file, and it will provide you with comprehensive information about all the running threads and the problems in the process.
There are various options available, but the following are some of the best online thread dump analyzers that you can use:
- Fast thread
- Spotify Thread Dump Analyzer
- Jstack review
- Site 24×7
This article discussed how a Java thread dump could help us analyze and point out any locked threads or deadlocks in your application. We reviewed how to analyze them properly by understanding and decoding the bulk of information present in the dump. With this information contained in the Java thread dump, you can quickly identify and resolve the causes of the problems in your Java application.