Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
hprof-analyzer
Your JVM left behind a multi-gigabyte .hprof heap dump — after an
OutOfMemoryError, a memory leak investigation, or just a routine heap snapshot.
You want to know what is in the heap without opening a large file in a GUI
or provisioning a machine as big as the dump.
hprof-analyzer is a command-line tool that reads the dump and writes a
self-contained report covering the same ground as Eclipse MAT's System
Overview, Leak Suspects, and Top Consumers analyses, plus additional views. Peak
RSS stays well below the dump size: on a 33 GiB dump it peaks at ~15 GiB where
MAT needs ~62 GiB (see Performance). The report is a single file you can email, attach to a
ticket, or diff in CI. For dumps up to 3 GB you can also run the analysis directly in
your browser — no install needed.
An experimental tool by the SapMachine team.
What you get
Run one command and get a report with these sections:
- System Overview: heap size, class/classloader breakdown, duplicate class definitions, GC roots, and a per-class histogram with a largest-instance column.
- Leak Suspects: objects retaining the most memory, each traced back to its GC root via the full reference chain.
- Top Consumers: classes, classloaders, and packages ranked by retained size (not just shallow), so allocations hidden inside containers show up under the right owner.
- Threads: stack frames and the local variables each thread keeps alive.
- Duplicate strings (opt-in,
--find-duplicates): wasted bytes from identicalStringvalues, top offenders, and which classes hold the most string references. - Collections analysis (opt-in,
--collections): fill ratios, size distributions, collision rates, and per-Class#fieldattribution for every Map, List, Set, and array. Covers standard JDK, Kotlin, and Eclipse Collections; custom types via TOML config.
Pick the format that fits: plain Markdown, Markdown with ASCII graphs (bars, sparklines, dominator trees), a self-contained HTML page you can open in any browser, or machine-readable JSON.
A live viewer shows all four output formats side by side, built from the public
Renaissance benchmark scala-doku dump:
➡ Open the sample report viewer
| Format | Default options | All optional features |
|---|---|---|
| Plain Markdown | scala-doku.md |
scala-doku-full.md |
| Markdown with ASCII graphs | scala-doku.graphs.md |
scala-doku-full.graphs.md |
| Self-contained HTML (opens live) | scala-doku.html |
scala-doku-full.html |
| Machine-readable JSON | scala-doku.json |
scala-doku-full.json |
Try it in the browser
Drop a .hprof file directly onto the page — the entire analysis runs in your
browser via WebAssembly, no install required. Heap dumps up to 3 GB are
supported.
| Landing page | OQL shell | Leak suspects report |
|---|---|---|
![]() |
![]() |
![]() |
Three modes are available after dropping a file:
- Analysis — histogram, leak suspects, dominator tree, GC roots, retained sizes
- Full Analysis — adds duplicate-string/array detection and collection fill-ratio
- OQL Shell — interactive query shell; named-query sidebar + tab-completion work offline
You can also connect to a locally running server for larger dumps:
All REPL commands (!top, !sort, !stats, !obj, …) and the full OQL
engine work the same way in the browser and in the CLI. Useful shell commands:
/help oql — full OQL language reference (works offline via WASM)
/examples — guided tour of OQL examples by category
/examples group — examples for GROUP BY / HAVING queries
Tab-completion and named-query browsing work fully offline via WASM.
Browser mode
Load web/dist/hprof-analyzer-browser.html in Chrome or Firefox — no install needed.
Drag and drop a .hprof file to analyze it entirely in-browser via WebAssembly.
Analysis modes:
- Full Analysis — complete report with dominator tree, leak suspects, top consumers, and interactive Heap Inspector
- Fast Analysis — skips retained-heap computation; histogram and OQL work but Leak Suspects are unavailable. Use for dumps > 2 GB.
Interactive features (WASM only):
- Heap Inspector — click any class or instance to open a panel with fields, GC root path, and peer navigation
- Field Scan — top instances of any class ranked by retained heap
- Object Graph Explorer — force-directed graph with edge labels and neighbor dimming
- OQL REPL — run queries against the loaded dump
Memory limits: up to ~3 GB HPROF files in Chrome (4 GB WASM address space). After full analysis, large arrays are deflate-compressed (~75% reduction) to fit within memory limits.
Quick start
Grab a prebuilt binary and analyze a dump in two commands. No Rust, no Node, no build step. Pick the line for your platform (see Install for all targets and other install methods):
# macOS (Apple Silicon)
|
# Linux (x86_64, glibc)
|
That unpacks a folder containing the hprof-analyzer binary. Run it on your
dump:
Open report.html in any browser. To run it from anywhere, move the binary
onto your PATH:
Compressed dumps are read transparently — no manual decompression needed:
| Format | Notes |
|---|---|
.hprof |
Raw dump |
.hprof.gz |
Gzip-compressed dump |
.hprof.zip |
ZIP archive containing the dump |
.hprof.tar.gz, .tar.gz, .tgz |
Gzip-compressed tar archive; the first .hprof entry is used |
Truncated and corrupt files are handled gracefully. If the JVM was killed mid-dump, the file was copied incompletely, the gzip stream ends early, or random bytes follow a valid HPROF header (e.g. a partial network copy), the analyzer recovers whatever objects were successfully parsed and produces a partial report rather than aborting with an error. A warning is printed to stderr and truncated_input: true is set in the JSON output when truncation or corruption is detected.
Analysis time scales with the dump — seconds for small dumps, minutes for multi-gigabyte ones (see Performance).
Why you might want it
- Memory-efficient and fast. Two-pass streaming keeps peak RSS well below the dump size and uses a fraction of what MAT needs — no heap-size flag to tune. See Performance for measured numbers.
- Broad JVM compatibility. Reads dumps from HotSpot, OpenJ9/IBM J9, and
Android ART. Handles all standard and JVM-specific HPROF sub-tags, including
ROOT_SYSTEM_CLASS(IBM J9) and the five Android ART-specific root and array tags. - Resilient against bad files. Truncated dumps, corrupt gzip streams, and malformed heap records all produce a partial report with a warning rather than a crash or error. Corrupt length fields are capped before allocation to prevent OOM.
- Scriptable and CI-friendly. Never prompts, never opens a window. Emit JSON, diff two dumps to catch memory growth in a pipeline, or gate a build on retained-size regressions.
- Emailable output. The HTML report is a single self-contained file with no server and no external assets — attach it to a ticket or share it as-is.
- Deterministic. Markdown output is byte-stable (modulo the generation timestamp), so it diffs cleanly across runs and across dumps.
When to use alternatives
This tool is deliberately narrow: it renders static replicas of the three views above plus threads, and nothing else. If you need to explore a heap — walk the dominator tree interactively, inspect arbitrary objects and their fields, or use the full breadth of MAT's analyses — reach for Eclipse MAT, the complete interactive GUI.
hprof-analyzer now also ships an OQL query engine (see
docs/OQL.md) and an HTTP server (server subcommand) for
programmatic access, so for scripting and LLM-assisted dump triage it is
often a better fit than MAT. Use MAT when you need interactive GUI exploration.
If all you need is a class histogram,
hprof-slurp is faster and lighter
because it never builds the dominator tree. But that also means it cannot report
retained sizes, leak suspects, root paths, or Top Consumers — the analyses
hprof-analyzer exists to provide.
Speeding up Eclipse MAT
If you use Eclipse MAT for interactive heap exploration, hprof-analyzer can dramatically reduce the time and memory needed for MAT's first open of a large dump.
MAT parses a 34 GB heap dump in ~4 s and writes 12 cache files alongside the
.hprof. On the next open it reads from cache and loads in ~0.9 s — but the
first parse peaks at ~55 GB RSS inside the JVM. hprof-analyzer generates
the same cache files in a single pass peaking at ~19 GB RSS:
# Generate MAT cache files (low RSS, no JVM tuning needed)
# Now open heap.hprof in MAT as usual — it detects the cache and skips parsing
MAT auto-detects the cache: if the index files are present and newer than the
.hprof, it prints "Reopening parsed heap dump file" and skips its own parser.
If you also want hprof-analyzer's own report, generate both in one pass (single hprof read, shared pipeline):
See docs/mat-cache.md for the full list of generated
files, known divergences from MAT's output, and the RSS budget details.
Install
Prebuilt binary (recommended)
No Rust, no Node.js. Download for your platform from the rolling
nightly
release (always tracks main):
| Platform | Archive |
|---|---|
| Linux x86_64 (glibc) | hprof-analyzer-x86_64-unknown-linux-gnu.tar.gz |
| Linux x86_64 (static musl) | hprof-analyzer-x86_64-unknown-linux-musl.tar.gz |
| Linux aarch64 (glibc) | hprof-analyzer-aarch64-unknown-linux-gnu.tar.gz |
| Linux aarch64 (static musl) | hprof-analyzer-aarch64-unknown-linux-musl.tar.gz |
| macOS (Apple Silicon) | hprof-analyzer-aarch64-apple-darwin.tar.gz |
| Windows x86_64 | hprof-analyzer-x86_64-pc-windows-msvc.zip |
Use the musl build on minimal containers or older distros (no libc dependency).
|
Already installed? Update to the latest nightly in one command:
To see your current version and what's available without updating:
With Cargo
Requires Rust 1.85+. If you don't have it, install rustup first:
|
From source
# binary at target/release/hprof-analyzer
Node.js/npm is only needed if you modify the web sources under web/src/.
Usage
hprof-analyzer <INPUT> [OUTPUT] [OPTIONS]
<INPUT> a .hprof, .hprof.gz, .hprof.zip, .hprof.tar.gz, .tar.gz, or .tgz heap dump
→ analyze it and write a report
a saved report .json[.gz] → re-render it to another format
Named subcommands:
compare Compare reports (MAT export vs ours, or two of ours across time)
completions Generate a shell completion script
mat Generate Eclipse MAT index cache files (low-RSS alternative to MAT's first parse)
query Run OQL queries against a heap dump (no full report needed)
server Serve OQL + report sections over HTTP
dev Developer / diagnostic commands
Analyze a dump
Output format is inferred from the extension; -f always wins. Stdout defaults
to plain Markdown.
Add --find-duplicates or --collections to enable the opt-in sections.
Progress on long runs is printed to stderr when it is a terminal; control
with --progress auto|always|never.
Flag reference
| Flag | Description |
|---|---|
--find-duplicates |
Detect content-identical String values and primitive arrays; reports wasted bytes and top offenders. Hashes to 64 bits — does not retain raw data. Adds a few extra heap scans. |
--collections |
Container attribution by holder Class#field: fill ratios, size distributions, collision rates. Adds ~300 MB peak RSS on large dumps. |
--obj-graph[=small|medium|large] |
Capture the outbound-reference graph for the top retained objects. Enables the interactive Object Graph Explorer in HTML reports. Implied by --full-analysis. |
--full-analysis |
Shorthand for --obj-graph --collections --find-duplicates. Adds ~330 MB peak RSS on large dumps. --ref-paths is excluded because it can add 100–500 MB on top; pass it separately when needed. |
--field-stats |
Per-class reference-field null/non-null/retained stats for the top-50 classes by instance count. Shows which fields are null-heavy or hold the most retained heap. |
--ref-paths |
Capture field-name labels on reference edges (~2 bytes/edge). Enables named field breakdowns in --field-stats and richer root paths. |
--reachable-only |
Restrict OQL results to GC-reachable objects (Eclipse MAT parity). Off by default so the report stays byte-stable. |
--detail minimal|default|max |
Adjust output-size caps (leak suspects, dominator subtree, alloc sites, thread locals, top consumers). See the detail preset table below. |
--mat <DIR> |
Emit Eclipse MAT-compatible binary index files into DIR alongside the normal analysis. See Speeding up Eclipse MAT. |
--query <OQL> |
Run an OQL query and embed results in the report. May be repeated. See OQL queries. |
--query-file <PATH> |
Read one OQL query per non-empty line from a file. |
--progress auto|always|never |
Control the live progress line on stderr. Default: auto (terminal only). |
Tune the report size with --detail
--detail |
root depth | alloc top | thread locals | dom nodes | dom depth | leak children | top consumers |
|---|---|---|---|---|---|---|---|
minimal |
10 | 15 | 5 | 500 | 10 | 15 | 10 |
default |
30 | 50 | 20 | 5,000 | 20 | 50 | 20 |
max |
200 | 500 | 100 | 100,000 | 50 | 500 | 100 |
--detail max raises the dominator-tree cap to 100k nodes and pushes peak RSS
higher on very large dumps.
Compare against a MAT export
Track growth across two dumps
Re-render a saved report
JSON schema
The JSON report format is described by docs/schema.json
(JSON Schema draft-2020-12). To regenerate it after model changes:
Shell completions
OQL queries (query subcommand)
Run SQL-flavoured queries against a heap dump without building a full report.
The query subcommand does a fast streaming parse and answers queries directly.
# Count all String instances
# Top 10 threads by shallow size
# Multiple queries in one pass
# Interactive REPL with tab-completion
Retained sizes (@retainedHeapSize), dominators, and reference-graph
attributes (@inbounds, @outbounds) require the full analysis pipeline
and are not available in the query subcommand. Pass --query /
--query-file to the main command instead (see below), or use the server
subcommand which unlocks retained-size queries after POST /analyze.
To embed queries in the full report, pass --query / --query-file to the
main analysis command:
The full OQL language reference — grammar, attributes, aggregates, visualization directives, worked examples — is in docs/OQL.md.
Visualization directives (-- @viz)
Prefix any query with a -- @viz comment to request a chart in the report:
Kinds: table (default), histogram, piechart, treemap. HTML renders
interactive charts; Markdown renders ASCII bars. The directive has no effect in
the query subcommand — it applies only to embedded queries in reports.
Use --query= (with =) to avoid clap misinterpreting the leading --
in the directive as a flag.
Compatibility with Eclipse MAT OQL
hprof-analyzer's OQL is modelled on Eclipse MAT's dialect and is largely
compatible. Key differences:
Extensions (not in MAT): MEDIAN/PERCENTILE aggregates, GROUP BY /
HAVING, path() reachability, -- @viz directives, arithmetic in SELECT
and WHERE, system-properties snapshot (@systemProperties), interactive REPL
with tab-completion, named queries library (!run <name>), report embedding
(--query / --query-file).
Behavioural differences: unreachable objects are included (MAT discards
them); s.count/s.offset are absent (modern JDK layout — use s.value,
s.coder); integer division by zero returns NULL instead of throwing;
toString() on non-String returns NULL (no live JVM reflection); get(n)
array/collection access and COUNT(*) FROM (subquery) are rejected.
Not yet supported: FROM OBJECTS <decimal-id> (hex works), array indexing
(s[0]), ${snapshot}.getClasses(). Some object-ref field navigations (where
the declared type is Object) silently return NULL — see docs/OQL.md §
Eclipse MAT OQL compatibility for
details and workarounds.
HTTP server (server subcommand)
server starts a lightweight HTTP API on 127.0.0.1 (loopback only) that
exposes OQL queries and all four report sections as JSON or Markdown endpoints.
Designed for use with LLM tooling, curl pipelines, and CI scripts that need
more than a static report.
The server prints a startup banner listing all available endpoints.
Key endpoints:
| Method | Path | Description |
|---|---|---|
| GET | /status |
{"status":"ready"|"analyzing"|"not_started"} |
| POST | /analyze |
Trigger full analysis (retained sizes, dominators) |
| POST | / |
Run OQL query → JSON |
| POST | /stream |
Run OQL query → NDJSON (streaming) |
| GET | /report |
Full report JSON (or ?format=md) |
| GET | /report/overview |
System overview section |
| GET | /report/leaks |
Leak suspects section |
| GET | /report/top |
Top consumers section |
| GET | /report/threads |
Thread overview section |
Lazy analysis: The first GET /report/… triggers analysis automatically.
Report endpoints return 202 Accepted while analysis is running; poll
GET /status until "ready".
Retained-size queries: At startup the server runs a fast query-only parse.
@retainedHeapSize and dominator attributes are available only after the full
analysis completes (via POST /analyze or an implicit trigger).
# Start server, trigger analysis, wait, then query
&
until | ; do ; done
# Report sections
|
# OQL query
See docs/OQL.md — server subcommand for the
full endpoint reference, body format, ?limit=N, NDJSON streaming, and error
response shapes.
Use with AI agents
hprof-analyzer works well as a tool for LLM agents. Start the server on your
dump, then point an agent at it:
A ready-made Claude Code skill is included at
skills/hprof-analyzer.md. Load it in Claude Code:
@skills/hprof-analyzer.md
"Connect to http://127.0.0.1:7070 and identify the top memory consumers"
The skill teaches Claude the server API, OQL syntax, and common heap-triage workflows. The OQL guide at parttimenerd.github.io/hprof-analyzer/oql/ covers grammar, examples, and the full attribute reference.
Performance
Measured on an AMD Ryzen Threadripper PRO 3995WX (64 cores / 128 threads) with
123 GiB RAM, Linux, commit 55eaca7,
2026-08-03. "Basic" = default analysis; "Full" = --full-analysis
(--obj-graph --collections --find-duplicates). MAT = Eclipse MAT 1.17.0
(ParseHeapDump.sh -Xmx80g). Wall-clock in m:ss or s. RSS is peak.
| Workload | Dump file | Wall basic | RSS basic | Wall full | RSS full | Wall (MAT) | RSS (MAT) |
|---|---|---|---|---|---|---|---|
| Renaissance scala-doku | 51 MiB | 2.4 s | 40 MiB | 5.7 s | 373 MiB | 0:04 | 1.63 GiB |
| gauss-mix | 70 MiB | 2.1 s | 44 MiB | 3.3 s | 195 MiB | 0:04 | 1.61 GiB |
| naive-bayes (1.3 GiB) | 1.3 GiB | 5.0 s | 67 MiB | 8.2 s | 382 MiB | 0:05 | 1.73 GiB |
| VS Code JVM (1.1 GiB) | 1.1 GiB | 0:43 | 216 MiB | 1:13 | 2.51 GiB | 0:28 | 2.39 GiB |
| HeapothesYs 16g | 11 GiB | 2:49 | 565 MiB | 4:07 | 6.32 GiB | 1:06 | 4.99 GiB |
| HeapothesYs 28g | 20 GiB | 5:31 | 1.05 GiB | 8:00 | 12.1 GiB | 2:10 | 5.27 GiB |
| Real-world 34g | 34 GiB | 22:26 | 7.78 GiB | — (OOM) | — | 2:19:17 | 5.18 GiB |
The 34g --full-analysis run OOM'd (12.1 GiB peak at 28g → ~18 GiB needed for 34g exceeds available headroom).
Basic analysis on 34g peaks at 7.78 GiB.
MAT was run with ParseHeapDump.sh -Xmx80g (leak-suspects + top-components).
MAT requires a JVM heap large enough to hold its in-memory index, so the RSS
reported here reflects a generously provisioned run. hprof-analyzer holds peak
RSS well below the dump size and needs no heap tuning. Correctness is validated
against MAT 1.17.0: the compare mat subcommand diffs a MAT System Overview
export against our JSON, and the parity fixtures gate on it (see
Compare against a MAT export).
How it works
The two-pass parser, the dominator-tree construction, the shallow/retained size formulas, and the compressed index structures are described in DESIGN.md.
Contributing
Contributions are welcome. See DESIGN.md for architecture context (two-pass parser, dominator-tree construction, size formulas, index structures).
Requires a stable Rust toolchain (1.85+); see Install. All commands from the repository root:
CI runs the same fmt, clippy, and test steps on stable. Parity fixtures
live under tests/fixtures/.
The HTML report embeds a pre-committed React bundle (web/dist/bundle.js), so
Node.js is not needed for normal builds. To rebuild it after changing
web/src/: cd web && npm install && npm run build.
The self-contained browser bundle is assembled from the WASM module and the
React bundle by web-browser/assemble.py:
# Build WASM module first
# Assemble the browser bundle
Support & Feedback
Bug reports, feature requests, and contributions are welcome via GitHub issues.
License
MIT. See LICENSE.
Copyright 2026 SAP SE or an SAP affiliate company, Johannes Bechberger and contributors.


