Heap Hunter End-User Guide
Everything you need to index, inspect, and diagnose Java heap dumps with Heap Hunter.
This guide assumes you have a pre-built hunt binary and a Java .hprof heap dump.
# 1. Files You Need in Your Working Directory
hunt looks for two files in the directory where you run it:
| File | Purpose |
|---|---|
license.dat | Signed license file |
state.dat | Supporting anchor for license file |
If either is missing, every command (except --help / --version) exits with a license error.
You should receive these from whoever provided the binary. If you are evaluating the project, copies exist in the repository under Licencing/tester/.
# macOS Gatekeeper Note
If you downloaded a prebuilt hunt binary (for example, hunt-aarch64-macos) on macOS, Gatekeeper may refuse to run it and report operation not permitted. This happens because downloaded executables are tagged with the com.apple.quarantine extended attribute, which the kernel enforces even for valid, ad-hoc signed Mach-O binaries.
Remove the quarantine flag:
xattr -d com.apple.quarantine /path/to/hunt
Then run the binary normally:
/path/to/hunt --help
# 2. Getting Help
# Top-level help
hunt --help
# Per-command help
hunt index --help
hunt histogram --help
hunt roots --help
The built-in help shows usage, options, examples, and exit codes for each command.
# 3. Optional: Create a Config File
To avoid typing --index-dir every time, create ~/.config/hunt/config.json:
mkdir -p ~/.config/hunt
cat > ~/.config/hunt/config.json <<'EOF'
{
"index-dir": "/path/to/your/indexes",
"memory-budget-mb": 4096
}
EOF
# Config behavior
| Field | Meaning |
|---|---|
index-dir | For index/overview, this is a base directory. |
memory-budget-mb | sorting budget and should not be changed |
Override the config path with --config /path/to/config.json, or override just the index dir with --index-dir <dir>.
# 4. The Standard Workflow
Heap Hunter is a two-pass tool:
index— scan the.hprofonce and write persistent index files.overview— build a smalloverview.bincache for fast summary queries.- Query commands — read from the index/cache.
You must run index, then overview, before any query.
# 4.1 Index the dump
hunt index path/to/heap.hprof --index-dir path/to/heap.idx
On success it prints a JSON summary:
{"status":"complete","index_dir":"path/to/heap.idx","n_objects":170877,"n_classes":1844,"n_edges":262839,"n_roots":1851,...}
If an up-to-date index already exists, it prints:
{"status":"reused","index_dir":"path/to/heap.idx"}
| Option | Meaning |
|---|---|
--index-dir <dir> | Where to write the index |
--memory-budget-mb <n> | RAM budget for sorting (default 4096) |
--force | Rebuild even if a matching index exists |
--no-progress | Suppress progress spinner |
# 4.2 Build the overview cache
hunt overview path/to/heap.hprof --index-dir path/to/heap.idx
This creates overview.bin inside the index directory.
Run it again after re-indexing, or use --force to overwrite.
#
5. Summary Commands (use overview.bin)
#
info — high-level dump summary
hunt info --index-dir path/to/heap.idx
Sample output:
Header:
id_size: 8 bytes
timestamp: 1783429843270
Summary:
total objects: 169033
classloaders: 4
classes: 1844
arrays: 67091
heap bytes: 10137438
#
histogram — top classes by shallow heap
# CSV output, top 15 by bytes
hunt histogram --index-dir path/to/heap.idx
# Human-readable table
hunt histogram --index-dir path/to/heap.idx --view
# Sort by instance count
hunt histogram --index-dir path/to/heap.idx --view -s
# Limit to 20
hunt histogram --index-dir path/to/heap.idx --view -l 20
Output columns: class, instances, shallow_bytes.
| Option | Meaning |
|---|---|
-l N, --limit N | Top N rows (default 15) |
--view | Formatted table instead of CSV |
-s, --sort-instances | Sort by instance count, not bytes |
--match <regex> | Include only matching class names |
--exclude <regex> | Omit matching class names |
--min-bytes <n> | Minimum total shallow bytes |
--min-instances <n> | Minimum instance count |
Regex notes: supports ^, $, ., *, +, ?, escaped literals, and character classes.
#
classes — loaded class metadata
hunt classes --index-dir path/to/heap.idx --view
Output columns: class, superclass, instance_size.
| Option | Meaning |
|---|---|
--match <regex> | Filter class names |
--exclude <regex> | Exclude class names |
--min-instance-size <n> | Minimum bytes per instance |
# 6. Object Query Commands (use the persistent index)
#
objects — list object IDs by class
hunt objects 'com.example.heapforge.User' --index-dir path/to/heap.idx --limit 10
Output is JSON:
{
"pattern": "com.example.heapforge.User",
"limit": 10,
"offset": 0,
"items": [
{"id": "0x0000000708827488", "ordinal": 4001, "class": "com/example/heapforge/User", "kind": "instance", "shallow_bytes": 85}
],
"next_offset": 1
}
| Option | Meaning |
|---|---|
--limit N | Max objects (default 100) |
--offset N | Skip first N matches |
--reverse | Descending ordinal order |
#
sample — decoded representative objects
hunt sample 'com.example.heapforge.User' --index-dir path/to/heap.idx --limit 3
Returns the same objects as objects, but with field names and values:
{
"items": [{
"id": "0x0000000708827488",
"class": "com/example/heapforge/User",
"shallow_bytes": 85,
"fields": [
{"name": "id", "value": 1756},
{"name": "name", "ref": "0x...", "ordinal": 3998, "class": "java/lang/String"},
{"name": "active", "value": true}
]
}]
}
#
object — decode one object by ID
hunt object 0x0000000708827488 --index-dir path/to/heap.idx
# 7. Reference-Tracing Commands
These commands walk the heap graph built during indexing.
#
refs-from — outgoing references
hunt refs-from 0x0000000708827488 --index-dir path/to/heap.idx --limit 10
Sample:
{
"object": "0x0000000708827488",
"direction": "outbound",
"items": [
{"id": "0x...", "ordinal": 3998, "class": "java/lang/String", "relation": "name"}
],
"next_offset": 5
}
#
refs-to — incoming references
hunt refs-to 0x0000000708827488 --index-dir path/to/heap.idx --limit 10
Use this to answer "who is holding this object alive?"
#
roots — shortest path to a GC root
hunt roots 0x0000000708827488 --index-dir path/to/heap.idx --max-visited 10000
This is the classic leak-analysis command. It performs a breadth-first search back to the nearest GC root and prints the path:
{
"found": true,
"truncated": false,
"path": [
{"id": null, "ordinal": 170877, "class": "virtual root"},
{"id": "0x...", "class": "com/example/heapforge/UserRepository"},
{"id": "0x...", "class": "java/util/concurrent/ConcurrentHashMap"},
{"id": "0x...", "class": "com/example/heapforge/User"}
]
}
| Option | Meaning |
|---|---|
--max-visited <n> | Cap BFS node visits |
#
tree — bounded expansion
# Outbound tree
hunt tree 0x0000000708827488 --index-dir path/to/heap.idx --direction from --depth 3 --max-nodes 50
# Inbound tree
hunt tree 0x0000000708827488 --index-dir path/to/heap.idx --direction to --depth 3 --max-nodes 50
| Option | Meaning | |
|---|---|---|
| `--direction from\ | to` | Follow outbound or inbound refs (default from) |
--depth <n> | Max depth | |
--max-nodes <n> | Max nodes in output |
# 8. Complete Example Session
# 1. Place heap dump file
#copy heap dump to the location where binary and license and state files exist
# 2. Index
hunt index production-heap.hprof --index-dir production-heap.idx
# 3. Overview
hunt overview production-heap.hprof --index-dir production-heap.idx
# 4. Summarize
hunt info --index-dir production-heap.idx
hunt histogram --index-dir production-heap.idx --view -l 20
# 5. Find suspicious class
hunt histogram --index-dir production-heap.idx --match '^com\.mycompany\.' --view -l 10
# 6. Sample instances
hunt sample 'com.mycompany.SessionContext' --index-dir production-heap.idx --limit 3
# 7. Pick an object and trace it
ID=$(hunt objects 'com.mycompany.SessionContext' --index-dir production-heap.idx --limit 1 | python3 -c "import sys,json; print(json.load(sys.stdin)['items'][0]['id'])")
hunt roots "$ID" --index-dir production-heap.idx --max-visited 50000
hunt tree "$ID" --index-dir production-heap.idx --direction to --depth 4 --max-nodes 100
# 9. Output Formats
| Command | Default | With --view |
|---|---|---|
index, overview | JSON | — |
info | Human table | — |
histogram, classes | CSV | Human table |
objects, sample, object | JSON | — |
refs-from, refs-to, roots, tree | JSON | — |
# 10. Environment Variables
| Variable | Effect |
|---|---|
NO_COLOR=1 | Disable colored output |
FORCE_COLOR=1 | Force colored output even when not a TTY |
XDGCONFIGHOME | Changes default config search path |
# 11. Common Errors
| Error | Cause | Fix |
|---|---|---|
license file not found | license.dat missing in cwd | Copy license file into cwd |
state file is corrupt | state.dat missing / tampered | Copy valid state.dat into cwd |
overview cache not found; run 'hunt overview ...' | Querying before overview | Run overview after index |
index not found; run 'hunt index ...' | Missing or wrong --index-dir | Check path, run index first |
object id not found in index | ID does not exist or wrong format | Use IDs returned by objects |
# 12. Quick Command Reference
| Command | What it does | Needs |
|---|---|---|
index <dump> | Build persistent index | license.dat, state.dat |
overview <dump> | Build overview.bin cache | Existing index |
info | Dump summary | Overview cache |
histogram | Class usage by size/count | Overview cache |
classes | Class metadata | Overview cache |
objects <regex> | Object IDs by class | Index |
sample <regex> | Decoded object samples | Index |
object <id> | Single object decode | Index |
refs-from <id> | Outbound refs | Index |
refs-to <id> | Inbound refs | Index |
roots <id> | Shortest path to GC root | Index |
tree <id> | Bounded ref tree | Index |
Ready to try Heap Hunter?
Join the waitlist