Debug and Monitor Java App with VisualVM and jstack

Anwar - Nov 3 - - Dev Community

Java applications, especially those running in production, can develop performance bottlenecks, deadlocks, and memory leaks that can be challenging to trace. When these issues arise, quick and effective debugging is essential to keep applications running smoothly and to maintain a positive user experience. This is where VisualVM and JStack become invaluable tools for Java developers.

VisualVM, with its powerful profiling and monitoring capabilities, and jstack, offering detailed thread dumps, are a dynamic duo that enables developers to detect, diagnose, and debug complex issues with ease.

Diagnosing and resolving deadlocks is critical, especially in high-performance applications. This guide will explore how to detect deadlocks and obtain thread dumps using two powerful Java tools: VisualVM and jstack.

1. Understanding Deadlocks and Thread Dumps

Before diving into the tools, let's clarify a few basics:

Deadlock: A situation where threads are waiting for each other to release resources, leading to an indefinite blocking cycle.

Thread Dump: A snapshot of the active threads within a Java application at a given time, including details on their states and any locks they are holding or waiting on. A thread dump is crucial in analyzing and identifying deadlock sources in Java applications, allowing developers to pinpoint the cause of blocked threads.

2. Introduction to VisualVM

VisualVM is a visual tool integrating several command-line JDK tools to deliver a comprehensive overview of Java applications. It’s widely used to monitor and profile applications, diagnose memory leaks, and analyze performance.

Key Features

  • Live monitoring and profiling
  • Thread analysis
  • Memory and CPU usage insights
  • Thread dumps for deadlock analysis

Debug using VisualVM (for Java applications)

  • Download VisualVM
  • Open VisualVM.
  • Connect to the running JVM process.
    Image description

  • In the monitoring tools, you can click thread dump or view thread details in real time.

Image description

Deadlock Thread Dump details

3. Introduction to jstack

jstack is an invaluable command-line tool for any Java developer dealing with complex applications that rely heavily on threads. By providing detailed insights into thread states, locking behavior, and execution flow, jstack simplifies the debugging process, making it easier to spot and resolve issues like deadlocks, performance bottlenecks, and application freezes.

Key Features

  • jstack shows the state of each thread, such as RUNNABLE, BLOCKED, WAITING, or TIMED_WAITING.
  • Capture stack traces for live or hung processes to help in debugging complex threading issues.
  • jstack marks deadlocked threads in the thread dump, allowing you to quickly identify them.
  • Obtain a thread dump, which lists all active threads in the JVM.
  • Paired with other tools like VisualVM or jmap, jstack can help trace memory leaks back to threads responsible for excessive object creation or holding onto references.
  • By analyzing thread dumps, developers can identify problematic synchronization, excessive blocking, or opportunities to reduce contention, leading to improved concurrency and responsiveness.

Using jstack to Obtain and Analyze Thread Dumps

To capture a thread dump of a running Java application using jstack, you’ll need the process ID (PID) of the Java process. Here’s a step-by-step guide:

  • Step 1: Find the Process ID (PID) of the Java Application
C:\Program Files\Java\jdk-21\bin>jps -l
12912
22480 org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp
24020 jdk.jcmd/sun.tools.jps.Jps
14344 org/netbeans/Main
21944 deadlock/deadlock.DeadlockExample
Enter fullscreen mode Exit fullscreen mode
  • Step 2: Use jstack to Capture the Thread Dump
C:\Program Files\Java\jdk-21\bin>jstack 21944
2024-11-02 11:12:18
Full thread dump Java HotSpot(TM) 64-Bit Server VM (21.0.5+9-LTS-239 mixed mode, sharing):

Threads class SMR info:
.
..
....
...

"Thread-0" #29 [18484] prio=5 os_prio=0 cpu=0.00ms elapsed=1896.34s tid=0x000001bb3395ac40 nid=18484 waiting for monitor entry  [0x00000099227ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at deadlock.DeadlockExample.lambda$0(deadlock/DeadlockExample.java:23)
        - waiting to lock <0x000000070d03c740> (a java.lang.Object)
        - locked <0x000000070d05a828> (a java.lang.Object)
        at deadlock.DeadlockExample$$Lambda/0x000001bb350019f8.run(deadlock/Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.5/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.5/Thread.java:1583)

"Thread-1" #30 [23240] prio=5 os_prio=0 cpu=0.00ms elapsed=1896.34s tid=0x000001bb3395b2a0 nid=23240 waiting for monitor entry  [0x00000099228ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at deadlock.DeadlockExample.lambda$1(deadlock/DeadlockExample.java:41)
        - waiting to lock <0x000000070d05a828> (a java.lang.Object)
        - locked <0x000000070d03c740> (a java.lang.Object)
        at deadlock.DeadlockExample$$Lambda/0x000001bb35001c08.run(deadlock/Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.5/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.5/Thread.java:1583)

...
.....
..
Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x000001bb117727a0 (object 0x000000070d03c740, a java.lang.Object),
  which is held by "Thread-1"

"Thread-1":
  waiting to lock monitor 0x000001bb11772500 (object 0x000000070d05a828, a java.lang.Object),
  which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-0":
        at deadlock.DeadlockExample.lambda$0(deadlock/DeadlockExample.java:23)
        - waiting to lock <0x000000070d03c740> (a java.lang.Object)
        - locked <0x000000070d05a828> (a java.lang.Object)
        at deadlock.DeadlockExample$$Lambda/0x000001bb350019f8.run(deadlock/Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.5/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.5/Thread.java:1583)
"Thread-1":
        at deadlock.DeadlockExample.lambda$1(deadlock/DeadlockExample.java:41)
        - waiting to lock <0x000000070d05a828> (a java.lang.Object)
        - locked <0x000000070d03c740> (a java.lang.Object)
        at deadlock.DeadlockExample$$Lambda/0x000001bb35001c08.run(deadlock/Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.5/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.5/Thread.java:1583)

Found 1 deadlock.

Enter fullscreen mode Exit fullscreen mode

Note

  • jstack is available in the JDK, so ensure that the JDK is installed and accessible in your environment.
  • Using jstack on some systems may require administrator privileges.
  • Using jstack -l 12345 > threaddump.txt saves the thread dump to a file called threaddump.txt.

Outro

VisualVM and jstack are two essential tools in the Java developer’s toolkit for diagnosing and troubleshooting Java applications, particularly when dealing with performance issues, deadlocks, and thread bottlenecks.

Together, VisualVM and jstack offer a comprehensive approach to debugging Java applications, with VisualVM providing broad, real-time performance insights and jstack enabling deep thread-level analysis. Their combined usage allows developers to effectively diagnose and resolve complex Java issues in both development and production environments.

Reference

A huge thanks to the online documentation, community and all the resources available that made this write-up possible.

  1. How to use VisualVM
  2. Multithreading Concepts Part 1: Atomicity and Immutability
  3. Java VisualVM
. . . . . . . .
Terabox Video Player