Both files get generated in the same bad moments — an outage, an OutOfMemoryError, a JVM that stopped answering — and the names are close enough that people search for one while needing the other. The distinction is worth thirty seconds, because analyzing the wrong snapshot wastes an hour: a thread dump tells you what the JVM is doing; a heap dump tells you what the JVM is holding.
This guide covers what each snapshot contains, which symptom calls for which, how to capture both without hurting production, and which tool reads each one. One honest note up front: ThreadMine analyzes thread dumps — for heap dumps we will point you at the right tools instead of pretending.
Two snapshots, two questions
- Thread dump — "what is every thread doing right now?" A text snapshot of each live thread: its stack trace, its state (RUNNABLE, BLOCKED, WAITING…), and the locks it holds or waits for. Kilobytes in size, captured in milliseconds. The tool for hangs, deadlocks, CPU spikes and exhausted pools.
- Heap dump — "what is filling the memory?" A binary snapshot (usually a
.hproffile) of every object in the heap: its class, its fields, and who references it. As large as the heap itself — often gigabytes. The tool for memory leaks andOutOfMemoryError: Java heap space.
The two are complementary, not rivals. A serious memory incident often uses both: the heap dump names the objects that filled the memory, and the thread dump shows which code paths were busy creating them when the music stopped.
What's inside a thread dump
One entry per thread, and each entry is a story: the thread's name, its state, the full stack of what it was executing, and the monitor/lock lines that connect it to other threads.
"http-nio-8080-exec-7" #41 daemon prio=5 tid=0x... nid=0x2f03 waiting for monitor entry java.lang.Thread.State: BLOCKED (on object monitor) at com.example.Cache.get(Cache.java:42) - waiting to lock <0x000000076ab00000> (a com.example.Cache) at com.example.Api.handle(Api.java:88)
Because it is plain text and costs almost nothing to produce, the standard advice is to capture several dumps a few seconds apart and compare them — a thread stuck on the same lock across three snapshots is a finding; a thread seen waiting once is just a Tuesday. Reading those patterns at scale is exactly what an analyzer automates: our complete guide to thread dump analysis walks through the manual version.
What's inside a heap dump
Every object alive in the heap at that instant, with its actual field values and the full reference graph — who points at whom. That graph is what makes memory-leak analysis possible: a memory analyzer computes the dominator tree and can say "this one HashMap retains 3.2 GB", or that a stopped classloader is still pinned by a single listener. None of that information exists in a thread dump — stacks reference code, not object contents.
The price is physics: the file is roughly as large as the used heap, writing it pauses the JVM, and opening it needs a serious workstation. A heap dump is a scalpel — precise, and not something you swing casually at production every few seconds.
Which one for which symptom
| Symptom | Capture this | Why |
|---|---|---|
| Application frozen / requests hang | Thread dump | The stacks show what every thread is stuck on — locks, IO, a downstream call. |
| Suspected deadlock | Thread dump | The JVM prints the deadlock cycle; the lock lines prove it. |
| CPU pinned at 100% | Thread dump | Repeated dumps reveal which threads stay busy and in which frames. |
| Worker pool exhausted, requests queueing | Thread dump | Counts every pool thread and shows what is holding all of them. |
| OutOfMemoryError: Java heap space | Heap dump | Only the object graph can say what filled the heap and who retains it. |
| Memory climbs until each restart | Heap dump | Two dumps taken hours apart, compared in a memory analyzer, expose the growth. |
| GC running constantly, app barely progressing | Heap dump + GC logs | The dump shows what the collector cannot free; the logs show the thrashing. |
| OutOfMemoryError: unable to create new native thread | Thread dump | Counter-intuitive but true: this OOM is a thread leak, and the dump counts the evidence. |
The last row is the one that surprises people: an OutOfMemoryError does not automatically mean "heap dump". When the message is unable to create new native thread, memory is fine — the process ran out of threads, and a thread dump showing 4,000 workers named pool-7-thread-N is the whole diagnosis.
How to capture each one
Thread dump — any of these, against the JVM's pid:
jstack -l <pid> > dump.txt # the classic jcmd <pid> Thread.print > dump.txt # the modern equivalent kill -3 <pid> # no JDK tools? dump goes to stdout
Take at least three, a few seconds apart. Finding the pid, Windows, containers and Spring Boot Actuator are covered in how to take a thread dump.
Heap dump — same tools, different verbs:
jcmd <pid> GC.heap_dump /tmp/heap.hprof # modern
jmap -dump:live,format=b,file=heap.hprof <pid> # classic ("live" runs a full GC first)
# insurance — set BEFORE the incident:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps ...That last flag is the cheapest observability you will ever configure: the JVM writes the heap dump at the exact moment of the OutOfMemoryError, when the evidence is still in the room.
Cost and safety in production
- Thread dump: take it freely. The safepoint pause is milliseconds, the file is small text, and repeated captures are not just safe — they are the recommended technique.
- Heap dump: plan for it. The JVM pauses while the entire heap is written to disk — seconds to minutes on large heaps — and the file needs as much free disk as the heap is big. On a struggling node, that pause is visible to users.
- They differ in sensitivity, too. A heap dump contains your actual data: strings, user records, tokens — whatever was in memory. Treat the file like a database extract. A thread dump exposes class names, stack traces and thread names — internal, but in a different league.
The right tool for each side
For heap dumps, the reference is Eclipse Memory Analyzer (MAT) — free, with a leak-suspects report and the dominator tree that does the retention math for you. VisualVM gives a lighter first look, and commercial profilers (JProfiler, YourKit) bundle the same analysis with their tooling. Hosted options exist as well (HeapHero, from the same family as fastThread). ThreadMine does not analyze heap dumps — if the file ends in .hprof, those are your tools.
For thread dumps, you can read the text by hand — every line of it — or hand the pattern-matching to a free online thread dump analyzer: paste the dump, no sign-up, and ThreadMine detects deadlocks, pool exhaustion, thread leaks and virtual-thread pinning automatically, grades the snapshot with a health score, and stitches multiple dumps into a timeline. Prefer reading by eye with a desktop tool? That works too — we compare ourselves honestly against IBM TMDA and jstack.review, including when they are all you need.
Whichever side of this page brought you here: capture the cheap snapshot first, keep the expensive one for memory-shaped symptoms, and let each file be read by the tool that was built for it.
Frequently asked questions
Can ThreadMine analyze heap dumps?
No — and this page exists so you find that out in ten seconds instead of after an upload. ThreadMine is a thread dump analyzer: send a thread dump from HotSpot, OpenJ9, Zing or GraalVM and it detects deadlocks, pool exhaustion, thread leaks and virtual-thread pinning automatically. For heap dumps (.hprof files), use a memory analyzer such as Eclipse MAT or VisualVM. Quick way to tell which file you have: a thread dump is readable text that starts with a timestamp and "Full thread dump"; a heap dump is a large binary file, usually named *.hprof.
Can a thread dump find a memory leak?
Not directly — object contents and references only exist in a heap dump, so memory-leak analysis belongs to a tool like Eclipse MAT. What a thread dump does find is a thread leak (thread count growing dump after dump), which also ends in OutOfMemoryError — but "unable to create new native thread" instead of "Java heap space". Repeated thread dumps can also show stacks busy in allocation-heavy code, which tells you where to point the heap analysis.
Which one should I capture first during an incident?
Thread dumps, almost always: they cost milliseconds, produce small text files, and you can take three of them a few seconds apart without anyone noticing. Capture a heap dump only when the symptom is memory-shaped (OutOfMemoryError, heap climbing until restart) — it pauses the JVM and writes a file as large as the heap. And run production with -XX:+HeapDumpOnOutOfMemoryError, so the JVM captures the heap dump for you at exactly the right moment.
Is it safe to take these snapshots in production?
A thread dump, yes: the safepoint pause is measured in milliseconds and the output is a small text file. A heap dump is heavier: the JVM pauses while it writes the whole heap to disk (seconds to minutes on multi-GB heaps), and the file needs that much free disk. Also treat the two files differently: a heap dump contains your actual object data — user records, tokens, anything in memory — so it is sensitive by definition; a thread dump contains stack traces and thread names, far less exposed.
Want to see all this applied to your own dump?
Upload a dump and get a health score, detected problems and fixes in seconds. No signup for the first dump.