Ponder End-User Guide
Everything you need to index, inspect, and diagnose Java heap dumps with Ponder.
This guide assumes you have a pre-built ponder binary and a Java .hprof heap dump.
# 1. Files You Need in Your Working Directory
ponder 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 ponder binary (for example, ponder-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/ponder
Then run the binary normally:
/path/to/ponder --help
# 2. Getting Help
# Top-level help
ponder --help
# Per-command help
ponder index --help
ponder histogram --help
ponder 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/ponder/config.json:
mkdir -p ~/.config/ponder
cat > ~/.config/ponder/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
Ponder 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
ponder 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
ponder 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
ponder 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
ponder histogram --index-dir path/to/heap.idx
# Human-readable table
ponder histogram --index-dir path/to/heap.idx --view
# Sort by instance count
ponder histogram --index-dir path/to/heap.idx --view -s
# Limit to 20
ponder 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
ponder 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
ponder 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
ponder 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
ponder 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
ponder 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
ponder 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
ponder 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
ponder tree 0x0000000708827488 --index-dir path/to/heap.idx --direction from --depth 3 --max-nodes 50
# Inbound tree
ponder 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
ponder index production-heap.hprof --index-dir production-heap.idx
# 3. Overview
ponder overview production-heap.hprof --index-dir production-heap.idx
# 4. Summarize
ponder info --index-dir production-heap.idx
ponder histogram --index-dir production-heap.idx --view -l 20
# 5. Find suspicious class
ponder histogram --index-dir production-heap.idx --match '^com\.mycompany\.' --view -l 10
# 6. Sample instances
ponder sample 'com.mycompany.SessionContext' --index-dir production-heap.idx --limit 3
# 7. Pick an object and trace it
ID=$(ponder objects 'com.mycompany.SessionContext' --index-dir production-heap.idx --limit 1 | python3 -c "import sys,json; print(json.load(sys.stdin)['items'][0]['id'])")
ponder roots "$ID" --index-dir production-heap.idx --max-visited 50000
ponder 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 'ponder overview ...' | Querying before overview | Run overview after index |
index not found; run 'ponder 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 Ponder?
Join the waitlist