jstack -l <pid> > dump.txt # -l adds java.util.concurrent lock owners
jcmd <pid> Thread.print -l > dump.txt # the same, through jcmd
kill -3 <pid> # printed to the JVM's stdout (container logs)
jcmd <pid> Thread.dump_to_file -format=json threads.json # JDK 21+: includes virtual threads
Java thread dump analyser
Paste jstack, jcmd Thread.print, kill -3 or Thread.dump_to_file (text or JSON, with virtual threads) output: threads grouped by state and identical stack, deadlocks and a lock graph, thread pools, hot frames, CPU per thread, and a comparison of dumps taken seconds apart to find stuck threads.
Added
Works with
- Java GC log analyser Databases and streaminghands a GC log dropped here to the GC log analyser, and opens thread dumps dropped there
- JVM memory planner for containers Databases and streamingsends this dump's thread count to the JVM memory planner, and NMT output pasted here
How to use it
A thread dump is the first thing I take when a Java service hangs or burns CPU, and the last thing I want to read line by line. This page reads it for you: it groups the threads, follows the locks and tells you what is stuck.
How to use it
Take a dump and paste it, drop the file, or open several files at once:
A file with several dumps in it (kill -3 a few times) is split into separate dumps. Example loads two real dumps of a small service I wrote for this page, taken 1.3 seconds apart.
-
Overview — threads by state, a bar of RUNNABLE / BLOCKED / WAITING / TIMED_WAITING, the findings (deadlocks, a thread holding a lock others wait for, saturated pools, threads using a lot of CPU), the thread pools recognised by name (Tomcat, Jetty, Undertow, HikariCP, Netty and Reactor Netty, ForkJoin, Executors, Spring, gRPC, Kafka, Lettuce…) and CPU time per thread where the dump has
cpu=andelapsed=(jstack from JDK 11 on). -
Threads — threads with identical stacks grouped, biggest group first, idle pool workers and JVM-internal threads last. Filter by name, frame or lock address, state, or what the thread is doing: running, blocked, waiting, sleeping, in I/O, idle (a pool worker waiting for work, a selector, accept) or JVM.
-
Locks — a graph of each contended lock: who holds it, who waits for it, and a table of every lock held. Monitors released by
Object.wait()are not counted as held. -
Deadlocks — cycles of threads each waiting for a lock the next one holds, drawn as a cycle, next to the JVM’s own "Found one Java-level deadlock" report when the dump has one. Without
-lthe dump doesn’t name the owners ofReentrantLockand friends; the page then uses the JVM’s report to fill them in. -
Hot frames — where the busy threads are: the top frame, and the first frame outside the JDK and common frameworks.
-
Compare — with two or more dumps: threads matched across dumps, flagged busy (same stack, CPU rising — a hot loop), stuck (same stack, blocked or in I/O or running without CPU), waiting or sleeping in the same place; CPU % of a core between the first and last dump; threads that appeared or went away.
Plan memory for N threads opens the JVM memory planner with the thread count. If you paste a GC log here by mistake, the page offers to open it in the GC log analyser; Native Memory Tracking output goes to the memory planner.
Formats
I checked the parser against dumps I captured from JDK 8, 11, 17, 21 and 24 (jstack, jstack -l,
jcmd Thread.print, kill -3 output, and Thread.dump_to_file text and JSON). The JSON format of
JDK 21–24 has no thread states or locks: states are then guessed from the top frame and marked
as such. Newer JDKs add state, parkBlocker, blockedOn and monitorsOwned; the page reads
them when they are there. IBM/OpenJ9 javacores are a different format and are not read.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.