hprof-analyzer 0.2.0

Fast, low-memory Java HPROF heap-dump analyzer with Eclipse MAT-parity reports (System Overview, Leak Suspects, Top Consumers).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# New Views & Additions — hprof-analyzer

Novel ideas for accelerating root-cause analysis of Java heap dumps.
Each entry has a description, mockup, data source, and implementation notes.

---

## 1. Retention Flamegraph (new section)

**What:** A flame-chart where each row is a dominator-tree depth level.
Width of each box is retained bytes. Hover to see class name + bytes.
Click to drill into that class's subtree. Similar to a CPU flamegraph but
for memory — the widest boxes at the top are your biggest leaks.

**Why it helps:** Instantly shows which class "owns" the most heap at each
depth, without scrolling through tables. The visual shape reveals clusters
of deep retention (long thin flames = linked lists; fat top = single big holder).

```
 Retained Flamegraph                                  [⬇ PNG] [Fit]
 ┌──────────────────────────────────────────────────────────────────┐
 │ depth 0 (GC roots)                                               │
 │ ████████████████████████████████████████████████████████████████ │  11.7 MB
 ├──────────────────────────────────────────────────────────────────┤
 │ depth 1                                                          │
 │ ████████████████████████ InTxnImpl     │ ████████ LazyVals$ │ ██ │
 │ 2.7 MB                                 │ 2.5 MB             │    │
 ├──────────────────────────────────────────────────────────────────┤
 │ depth 2                                                          │
 │ ████████████████ Object[]  │ ████████ HashMap │ █ String[] │ ██  │
 ├──────────────────────────────────────────────────────────────────┤
 │ depth 3                                                          │
 │ ███████████ HashMap$Node   │ ████ String  │ █ byte[]       │ ██  │
 ├──────────────────────────────────────────────────────────────────┤
 │ depth 4                                                          │
 │ ████ ZipFile$Source │ ████ SoftReference │ ██ JarFile │ ██ char[]│
 └──────────────────────────────────────────────────────────────────┘
  Each box: click to select class  · hover for exact bytes & %
  Color = class package (same hue = same package)
```

**Data:** `idom_pairs` + `biggest_classes` (retained by depth level).
Can approximate depth from BFS over `idom_pairs` graph.
**Placement:** New section between Dominator Analysis and Threads.

---

## 2. Retention Heatmap Matrix (new section)

**What:** A matrix where rows = top dominator classes, columns = top dominated
classes. Each cell is colored by retained bytes flowing from row→column.
Essentially a visual version of the `idom_pairs` table.

**Why it helps:** Reveals cross-class retention patterns in one glance — e.g.,
"HashMap$Node dominates String in 3 different paths" shows up as a hot cell.
Identifies unexpected class relationships (e.g. ZipFile holding InTxnImpl).

```
 Retention Heatmap  (top 15×15 by retained flow)      [Show as KB]
 ┌────────────────┬──────────────────────────────────────────────────────────┐
 │  dominator↓    │ Object[] HashMap HM$Node  String  byte[]  char[] ZipFile │
 │ dominated→     │                                                           │
 ├────────────────┼──────────────────────────────────────────────────────────┤
 │ InTxnImpl      │  ████░░░  ███████  ████    ██████   ████   ░░░   ░░░░░  │
 │ LazyVals$      │  ████████ ██░░░░░  ███░    ███░░░   ██░░   ░░░   ░░░░░  │
 │ Class          │  ░░░░░░░  ░░░░░░░  ███░    ████████ ██░░   █████ ░░░░░  │
 │ ZipFile$Source │  ░░░░░░░  ░░░░░░░  ░░░░    ░░░░░░░  ██████ ░░░░ ████████│
 │ SoftReference  │  ░░░░░░░  ░░░░░░░  ░░░░    ░░░░░░░  ░░░░░  ░░░░ ░░░░░  │
 └────────────────┴──────────────────────────────────────────────────────────┘
  ████ = high retained  ░░░░ = low/none   Click cell → filter graph to pair
```

**Data:** `idom_pairs` table directly.
**Placement:** Inside Dominator Analysis section, tab alongside graph.

---

## 3. Class Relationship Explorer ("Who holds what" diagram)

**What:** Pick any class. Shows a two-ring diagram:
- Inner ring: the selected class
- Middle ring: all classes that DOMINATE it (what holds it in memory)
- Outer ring: all classes it DOMINATES (what it retains)
Size of each arc = retained bytes. Clicking an arc selects that class and
recenters the diagram.

**Why it helps:** Answers "what is holding X in memory and what does X hold?"
in one visual — the key question when investigating a suspect.

```
 ┌─────────────────────────────────────────────────────────────────┐
 │  Class:  [ InTxnImpl              ▼ ]   [← Back]  [Inspect →]  │
 │                                                                  │
 │                ╭──────────────────────────╮                      │
 │    InTxnImpl   │      HELD BY             │ Object[] 2.1MB      │
 │    2.7 MB      │   (dominators)           │ Class    1.8MB      │
 │                │ ╭─────────────╮          │ HashMap  0.9MB      │
 │                │ │  ▓▓▓▓▓▓▓▓▓ │          │                      │
 │                │ │ InTxnImpl  │          │ RETAINS              │
 │                │ │  2.7 MB   │          │ (dominated)          │
 │                │ ╰─────────────╯          │ String   1.2MB      │
 │                │                          │ byte[]   0.7MB      │
 │                ╰──────────────────────────╯ char[]   0.4MB      │
 │                                                                  │
 │  ↑ Ancestors (3)  ↓ Dominated (8 classes · 2.7 MB total)       │
 └─────────────────────────────────────────────────────────────────┘
```

**Data:** `idom_pairs`.
**Placement:** Replaces/augments the "Who Holds This Class? Navigator" section,
or as a new tab inside Dominator Analysis.

---

## 4. Object Graph: Path Between Two Classes (new feature in OGE)

**What:** In the Object Graph Explorer, add a "Find path" mode:
select a source class and target class, and the UI runs BFS over
`idom_pairs`/`obj_graph_flat` to find the shortest dominator chain
between them, then highlights that path in the graph.

**Why it helps:** When you see "Class A is leaking 5 MB" and you know
"it comes from Class B", the current UI requires manual clicking to find
the chain. Path-finding automates this.

```
 ┌─────────────────────────────────────────────────────────────────────┐
 │  Find Retention Path                                                 │
 │                                                                      │
 │  From: [ InTxnImpl                    ▼ ]                           │
 │    To: [ ZipFile$Source               ▼ ]                           │
 │                                           [Find Path]               │
 │  ────────────────────────────────────────────────────────────────   │
 │  Shortest path (4 hops):                                            │
 │                                                                      │
 │   InTxnImpl ──[txnContext]──▶ HashMap ──[table]──▶ HashMap$Node     │
 │       2.7 MB                    1.5 MB               1.6 MB          │
 │        ↓                                                             │
 │   HashMap$Node ──[value]──▶ ZipFile$Source                          │
 │       1.6 MB                    1.3 MB                               │
 │                                                                      │
 │   [Jump to path in graph]   [Copy as text]                          │
 └─────────────────────────────────────────────────────────────────────┘
```

**Data:** `idom_pairs` for class-level BFS; `obj_graph_flat` for
instance-level path.
**Placement:** New toolbar button in DomGraphView and ObjectGraphExplorer.

---

## 5. Heap Timeline Sparklines in Class Histogram (enhancement to System Overview)

**What:** In the biggest-classes table, each row gets a tiny sparkline bar
showing retained-size ranking change vs. known reference points.
More importantly: add a "suspicious growth rate" flag column that marks
classes whose retained share is disproportionately large relative to
instance count (potential interning / caching accumulation pattern).

**Why it helps:** Raw retained bytes don't tell you if a class is "normal"
or "suspicious". A class with 94 instances holding 2.7 MB has a very high
bytes-per-instance ratio that often signals a cache or registry leak.

```
 Biggest Classes by Retained                    [Show KB] [⎘ TSV]
 ┌──┬───────────────────────────────┬────────┬────────┬─────────┬─────────┐
 │ #│ Class                         │Objects │Shallow │Retained │ B/inst  │
 ├──┼───────────────────────────────┼────────┼────────┼─────────┼─────────┤
 │ 1│ InTxnImpl              ⚠ high │    94  │ 13 KB  │ 2.7 MB  │  29 KB ↑│
 │ 2│ LazyVals$              ⚠ high │     1  │  32 B  │ 2.5 MB  │ 2.5 MB ↑│
 │ 3│ Class                         │ 2,793  │ 33 KB  │ 1.5 MB  │   550 B │
 │ 4│ Object[]                      │ 5,829  │  2 MB  │ 1.1 MB  │   193 B │
 │ 5│ ZipFile$Source         ⚠ high │    19  │  1 KB  │ 1.3 MB  │  69 KB ↑│
 └──┴───────────────────────────────┴────────┴────────┴─────────┴─────────┘
  ⚠ high = retained/instance >10× median for that instance count bucket
```

**Data:** `biggest_classes` + simple ratio computation.
**Placement:** Inline addition to the existing biggest-classes table.

---

## 6. GC Root Heatmap (new section / enhancement to System Overview)

**What:** A compact matrix of GC root types (rows) vs top retained classes
(columns). Each cell is a dot sized by retained bytes. Instantly shows
"Thread locals hold InTxnImpl" or "Static fields hold ZipFile$Source".

**Why it helps:** The existing GC root section just shows counts. This
shows WHICH classes are retained by WHICH root type — the direct answer
to "where is this leak anchored".

```
 GC Root Retention Matrix                                  [Show KB]
 ┌──────────────────────┬────────────────────────────────────────────────┐
 │ Root type            │  InTxn  LazyVals  ZipFile  Class  HashMap  Str │
 ├──────────────────────┼────────────────────────────────────────────────┤
 │ Thread (local var)   │  ●●●●●   ○○○○○○   ○○○○○   ○○○○○   ●●○○○  ○○○ │
 │ Static field         │  ●●○○○   ●●●●●●   ●●○○○   ●●●○○   ●●○○○  ○○○ │
 │ JNI global ref       │  ○○○○○   ○○○○○○   ●●●●●   ○○○○○   ○○○○○  ○○○ │
 │ System class         │  ○○○○○   ○○○○○○   ○○○○○   ●●●●●   ○○○○○  ●○○ │
 └──────────────────────┴────────────────────────────────────────────────┘
  ●●●●● = high retention   ○○○○○ = none   Click cell → filter Object Graph
  Hover = exact bytes · class pair
```

**Data:** `overview.gc_root_retained` (already has per-root-type class breakdown).
**Placement:** Inside System Overview, after GC root type bar chart.

---

## 7. Dominator Graph: Pinned "Blame View" (new focus mode in DomGraphView)

**What:** A new focus mode button "📌 Blame" that, when clicked, locks the
graph into a top-down tree showing only the highest-retained-bytes path from
every GC root down to the deepest node. Think of it as "critical path" of
retention — the nodes you must free to reclaim the most memory. Non-critical
nodes collapse to a "N others" group.

**Why it helps:** In a graph with 50 nodes, you can't tell which path is
"the" retention path. Blame mode reduces it to 5–8 nodes along the
dominant chain.

```
 [Force] [Tree] | [All] [▲ Retained by] [▼ Retains] [📌 Blame] | ...

 📌 Blame view — critical retention path (top 94.1% of heap)

                 ┌─────────────────┐
          ┌──────│  (GC roots)     │──────┐
          │      │   11.7 MB 100%  │      │
          │      └─────────────────┘      │
          ▼                               ▼
  ┌──────────────┐                ┌──────────────┐
  │  InTxnImpl   │                │  LazyVals$   │
  │  2.7MB 22.9% │                │  2.5MB 21.5% │
  └──────┬───────┘                └──────┬───────┘
         │                               │
         ▼                               ▼
  ┌──────────────┐                ┌──────────────┐
  │  Object[]    │                │  Object[]    │
  │  1.1MB  9.5% │                │   940KB  8%  │
  └──────────────┘                └──────────────┘
         └──── [+7 collapsed: 450KB] ───┘
```

**Data:** `idom_pairs` — walk the max-retained-child path from each root.
**Placement:** New toggle button in DomGraphView toolbar.

---

## 8. Instance Scatter Plot (new viz in Inspector / Top Consumers)

**What:** For a selected class, plot each instance as a dot on a 2D chart:
X = shallow size, Y = retained size. Color = GC root distance (depth).
Outlier instances (top-right quadrant) are your biggest individual memory
holders. Click a dot to open that instance in the Inspector.

**Why it helps:** "94 InTxnImpl instances hold 2.7 MB total" — but is it
distributed evenly, or are 2-3 instances holding most of it? The scatter
plot answers this in 1 second.

```
 InTxnImpl — Instance Scatter (94 objects)        [Open in Inspector →]
 retained
 2.5MB │                                              ●  (outlier!)
 500KB │                          ●●  ●●●
       │                    ●●●●●●●●●●●●●●
 100KB │              ●●●●●●●●●●●●●●●●●●●●●
       │         ●●●●●●●●●●●●●●●●●●●
  10KB │   ●●●●●●●●●●●●●
       └──────────────────────────────────────────→  shallow
          80B     200B   400B     800B   1.6KB
  Color: ■ depth 1  ■ depth 2  ■ depth 3+
  Hover = object address · retained · held-via field
  Click = open in Inspector
```

**Data:** Requires iterating `obj_graph_flat.nodes` filtered by class.
**Placement:** New "Scatter" tab in the Inspector class view, or button
in biggest-classes table row.

---

## 9. Retention Flow Sankey (new section or tab in Dominator Analysis)

**What:** A Sankey diagram where flow width = retained bytes.
Left side = GC root types, middle = top retainer classes, right = what
they retain. This gives the full "money trail" — from thread statics
through intermediate holders down to the objects actually consuming memory.

Currently the Leak Suspects section has a Sankey for merged paths, but
this would be a GLOBAL one covering all retention flows, not just suspects.

**Why it helps:** The existing dominator graph is undirected-looking and
cluttered. A Sankey forces a left→right flow reading that is much faster to
parse for "where does my memory come from".

```
 Retention Flow                                           [Show KB]
 ┌─────────────────────────────────────────────────────────────────┐
 │                                                                  │
 │  Thread locals ────╗                 ╔═══ InTxnImpl  2.7MB ─── │
 │              2.7MB ╠══ InTxnImpl ════╣                         │
 │                    ║                 ╚═══ HashMap    1.2MB ─── │
 │  Static fields ────╣                                            │
 │              2.5MB ╠══ LazyVals$ ════════ Object[]  2.5MB ─── │
 │                    ║                                            │
 │  JNI globals  ─────╣                 ╔═══ ZipFile$Src 1.3MB ── │
 │              1.5MB ╠══ Class ════════╣                         │
 │                    ║                 ╚═══ String    1.2MB ─── │
 │  System class ─────╝                                            │
 └─────────────────────────────────────────────────────────────────┘
  Hover node = select class  · Hover edge = exact bytes
```

**Data:** `gc_root_retained` (root type → classes) + `idom_pairs`
(class → dominated classes).
**Placement:** New "Sankey" tab inside Dominator Analysis graph panel.

---

## 10. Thread → Retained-Class Breakdown (enhancement to Threads section)

**What:** Each thread row expands to show a stacked bar of which CLASSES
it retains (not just total bytes). This answers "what is Thread-47 holding?"
without opening the object graph.

**Why it helps:** Multiple threads holding identical class mixes = one shared
data structure. One thread with a unique mix = that thread is the leak origin.

```
 Threads by Retained Heap                              [Show KB]
 ┌────────────────────┬──────────┬──────────────────────────────────┐
 │ Thread name        │ Retained │ Top retained classes             │
 ├────────────────────┼──────────┼──────────────────────────────────┤
 │▶ main              │ 302 KB   │ ▓▓▓▓▓ byte[] ▒▒▒ HashMap ░ ...  │
 │▶ Thread-259        │ 155 KB   │ ▓▓▓▓▓▓▓▓▓▓▓▓ Object[] ░░ ...   │
 │▶ Thread-172        │  29 KB   │ ████ InTxnImpl ██ String □ ...  │
 │▶ Thread-223        │  29 KB   │ ████ InTxnImpl ██ String □ ...  │
 │▶ Thread-230        │  29 KB   │ ████ InTxnImpl ██ String □ ...  │
 └────────────────────┴──────────┴──────────────────────────────────┘
  Thread-172/223/230 have identical class distribution → shared data structure
  [↓ Merge identical threads] — groups threads with matching top-5 class mix
```

**Data:** `thread_overview.threads` (locals) + `obj_graph_flat` dom subtree.
Can approximate from `thread_local_analysis`.
**Placement:** Inline bar added to existing threads table.

---

## 11. "Quick Path" Inspector Widget (enhancement to HeapInspector)

**What:** In the Inspector side panel, add a persistent "Retention Path"
minimap at the top of every view. It shows the 3–5 hop dominator chain
from the current object up to its GC root, always visible as a breadcrumb
with retained sizes. Each hop is a clickable button.

**Why it helps:** When drilling into an instance, you lose context of how
you got there. The minimap keeps the full context visible at all times.

```
 ┌─────────────────────────────────────────────────────────────────┐
 │ Inspector                                          [← Back] [✕] │
 ├─────────────────────────────────────────────────────────────────┤
 │ Retention path (GC root → this):                               │
 │  [Thread: main]  →  [InTxnImpl]  →  [HashMap]  →  [this obj]  │
 │   302 KB              2.7 MB          1.5 MB        248 B      │
 ├─────────────────────────────────────────────────────────────────┤
 │ java.util.HashMap                                               │
 │ Object #0x7f2a3b80  ·  shallow: 48 B  ·  retained: 1.5 MB      │
 │ ...                                                             │
```

**Data:** `obj_graph_flat` dom path (walk idom chain from current node to root).
**Placement:** Top of HeapInspector panel, persists across all inspector views.

---

## 12. Class Similarity Clusters (new section)

**What:** Group classes by structural similarity: classes with the same
retained/shallow ratio, same instance count order of magnitude, or that
frequently appear together in dominator chains. Display as a visual
cluster grid. Highlight "unusual" classes that don't fit any cluster
(anomaly detection).

**Why it helps:** In a complex heap, the leak is often ONE class that
doesn't belong with its neighbors. Clustering makes the outlier stand out.

```
 Class Clusters (k=5, by retained/instance profile)
 ┌────────────────────────────────────────────────────────────────┐
 │  Cluster A: "Cache entries" (high ret/instance, growing)       │
 │  ┌─────────────┐ ┌────────────┐ ┌──────────────────────┐      │
 │  │ InTxnImpl   │ │ LazyVals$  │ │  ZipFile$Source      │      │
 │  │ 94 inst     │ │ 1 inst     │ │  19 inst             │      │
 │  │ 29KB/inst ⚠ │ │ 2.5MB/inst │ │  69KB/inst ⚠        │      │
 │  └─────────────┘ └────────────┘ └──────────────────────┘      │
 │                                                                 │
 │  Cluster B: "Container overhead" (shallow ≈ retained)          │
 │  ┌─────────────┐ ┌────────────┐ ┌──────────────────────┐      │
 │  │ HashMap     │ │ HashMap$   │ │  Object[]            │      │
 │  │             │ │  Node      │ │                      │      │
 │  └─────────────┘ └────────────┘ └──────────────────────┘      │
 │                                                                 │
 │  Cluster C: "String infrastructure" (text/encoding overhead)   │
 │  ┌─────────────┐ ┌────────────┐ ┌──────────────────────┐      │
 │  │ String      │ │ byte[]     │ │  char[]              │      │
 │  └─────────────┘ └────────────┘ └──────────────────────┘      │
 └────────────────────────────────────────────────────────────────┘
  Click cluster = filter dominator graph to those classes
  ⚠ = anomaly: outlier from expected retained/instance for class type
```

**Data:** `biggest_classes` + `idom_pairs`. K-means on (log(instances),
log(retained/instances)) in the browser.
**Placement:** New section after Top Consumers.

---

## 13. Leak Score Dashboard (enhancement to Memory Triage / new landing section)

**What:** A single-screen "dashboard" card that scores each class on a
composite leak probability score (0–100), combining:
- retained/instance ratio vs median
- participation in dominator chains (is it a hub?)
- GC root distance (closer = more likely to be a leak anchor)
- depth below root (shallower = more likely to be the root cause)

Displayed as a ranked scorecard. Click any row to jump to that class in
all relevant sections.

**Why it helps:** Users currently have to cross-reference 4–5 different
tables to form a mental model. The score card does it automatically.

```
 Leak Score                                              [How scored?]
 ┌───┬────────────────────────────┬───────┬──────────────────────────────┐
 │ # │ Class                      │ Score │ Signals                      │
 ├───┼────────────────────────────┼───────┼──────────────────────────────┤
 │ 1 │ InTxnImpl                  │  94 ● │ ↑ret/inst  ↑hub  ↑depth      │
 │ 2 │ ZipFile$Source             │  87 ● │ ↑ret/inst  ↑depth            │
 │ 3 │ LazyVals$                  │  82 ● │ ↑ret/inst  ↑shallow-depth    │
 │ 4 │ SoftReference              │  71 ○ │ ↑hub  soft-ref-held          │
 │ 5 │ HashMap                    │  45 ○ │ ↑hub  normal-ratio           │
 │ 6 │ String                     │  12 · │ normal-ratio  low-depth      │
 └───┴────────────────────────────┴───────┴──────────────────────────────┘
  ● high confidence  ○ moderate  · likely benign
  Click row → highlights class in Dominator Graph, Triage, Inspector
```

**Data:** Derived from `biggest_classes`, `idom_pairs`, `triage`.
**Placement:** New card inside Memory Triage section, or standalone
"Leak Score" section.

---

## 14. Dominator Graph: Overlay Mode (enhancement to DomGraphView)

**What:** Add an "Overlay" dropdown to the dominator graph toolbar that lets
you color nodes by a secondary metric:
- **Default:** package-based color (current)
- **% Heap:** gradient from blue (low) to red (high % of heap)
- **B/instance:** retained bytes per instance (catches caches)
- **GC distance:** hops to nearest GC root (red = close to root = anchor)
- **Depth:** position in dominator tree depth (rainbow gradient)

**Why it helps:** The current graph is colored by package, which is
aesthetically nice but doesn't encode any leak-relevant information.
Switching to "% Heap" overlay immediately makes the biggest leaks pop
as bright red nodes.

```
 [Force] [Tree] | [All] [▲ Ret by] [▼ Retains] | [% Labels] [Edge Labels]
 Color: [Package ▼]  ← dropdown: Package / % Heap / B/instance / GC dist / Depth
        ┌─────────────────────────────────────────────────────────────────┐
        │  % Heap overlay:                                                 │
        │                          ●●  (InTxnImpl, 22.9%, red)           │
        │         ●               ● ●  (Object[], 9.5%, orange)          │
        │       ●   ●          ●                                          │
        │     ●       ●      ●   ●      (HashMap, 5.7%, yellow)          │
        │       ●         ●●●                                             │
        └─────────────────────────────────────────────────────────────────┘
  ■ red = >10%   ■ orange = 5–10%   ■ yellow = 1–5%   ■ blue = <1%
```

**Data:** `idom_pairs` + `biggest_classes` for node enrichment.
**Placement:** New "Color:" dropdown in DomGraphView toolbar.

---

## 15. Allocation Site → Retained Size Join (enhancement to Allocation Sites)

**What:** If allocation sites are present, join them with the retained-size
data: for each stack trace, show not just shallow bytes at allocation time,
but current retained bytes for objects allocated there. This reveals
"allocated here, never freed" patterns directly.

**Why it helps:** Allocation sites currently show shallow bytes at capture.
Joining with retained shows which allocation sites are contributing to live
memory pressure — the leak origin.

```
 Allocation Sites                              [Filter by class…]
 ┌────────────────────────────────────────────────────────────────┐
 │ #1  InTxnImpl  94 objects  13 KB shallow  2.7 MB retained ⚠   │
 │     scala.concurrent.stm.ccstm.InTxnImpl.<init>(InTxnImpl.sc:42│
 │       ← scala.concurrent.stm.ccstm.TxnLevelImpl.begin(:88)    │
 │         ← scala.concurrent.stm.ccstm.CCSTMExecutor.apply(:201) │
 │     Retained/allocated: ████████████████████████ 20,800×       │
 │     [Inspect class]  [Show in dominator graph]                 │
 ├────────────────────────────────────────────────────────────────┤
 │ #2  ZipFile$Source  19 objects  1 KB shallow  1.3 MB retained ⚠│
 │     java.util.zip.ZipFile$Source.<init>(ZipFile.java:581)      │
 │     [Inspect class]  [Show in dominator graph]                 │
 └────────────────────────────────────────────────────────────────┘
  ⚠ = retained >> shallow (object still live, data accumulated after alloc)
```

**Data:** `alloc_sites` joined with `biggest_classes` by class name.
**Placement:** Enhancement to existing AllocSitesSection.

---

## Implementation Priority

Ranked by impact vs effort:

| # | View | Impact | Effort | Notes |
|---|------|--------|--------|-------|
| 14 | Overlay mode (color by % heap, B/inst) | ★★★★★ | Low | Just changes node color mapping |
| 5 | B/instance ⚠ flag in classes table | ★★★★☆ | Low | One computed column |
| 6 | GC root heatmap matrix | ★★★★☆ | Medium | Needs data join |
| 13 | Leak score dashboard | ★★★★☆ | Medium | Computed from existing data |
| 11 | Quick path breadcrumb in Inspector | ★★★★☆ | Medium | Already have idom chain |
| 1 | Retention flamegraph | ★★★★☆ | High | New D3/canvas component |
| 7 | Blame view in DomGraph | ★★★★☆ | Medium | Max-path walk in existing graph |
| 10 | Thread → class breakdown bars | ★★★☆☆ | Medium | Needs thread-local data join |
| 4 | Path between two classes | ★★★☆☆ | Medium | BFS over idom_pairs |
| 9 | Global Sankey | ★★★☆☆ | High | Already have sankey library |
| 2 | Heatmap matrix | ★★★☆☆ | Medium | Canvas heatmap |
| 8 | Instance scatter plot | ★★★☆☆ | Medium | Needs per-instance data |
| 12 | Class clusters | ★★☆☆☆ | High | K-means in browser |
| 3 | Class relationship diagram | ★★☆☆☆ | Medium | Redundant with current graph |
| 15 | Alloc site + retained join | ★★★☆☆ | Low | Simple table join |