You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Say "Stuck" and "Expected", and name the faulty reference
No word built on "leak" belongs on an object. A leak is one faulty
reference that should have been cleared, and everything under it is
retained by that single mistake — so "Leaked" on twenty objects points a
reader at the twenty rather than at the one thing to fix. "Stuck" says
the object's situation without accusing it, and it is the only candidate
that asks a question instead of closing one: something is holding this,
what? "Expected" says its presence in memory is legitimate at this point
in the app's life.
No other analyser has a verdict like this to borrow words from, checked
before choosing: JProfiler classifies objects by reference type and by
age, YourKit by reachability scope, MAT and dotMemory name places rather
than objects. What they share is the frame — JProfiler asks whether
objects "are still legitimately on the heap or if a faulty reference
keeps them alive" — which is where the name for the culprit edge comes
from, and it is now what the leaks screen and the KDoc call it.
An object nothing reaches any more stays Stuck like any other: it was
expected to be gone, and only the collector not having run keeps it
here. A fourth value would have made the verdict mean something
different in one corner of the window.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/shark-explorer.md
+34-28Lines changed: 34 additions & 28 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,8 +70,8 @@ a few seconds.
70
70
Java heap from API 26 to 34, and for those the app offers to fetch them off the device the dump came from.
71
71
***Object list** is the whole dump as a searchable list, and **Starred** keeps the objects you want to
72
72
come back to.
73
-
***Whether the object a tab is on leaked is the first thing "What it is" says**, and you can overrule it —
74
-
see [Say what leaked](#say-what-leaked).
73
+
***The verdict on the object a tab is on is the first thing "What it is" says** — `Stuck`, `Expected` or
74
+
`Unknown` — and you can overrule it, see [The verdict](#the-verdict).
75
75
* Every location takes a **note**, in markdown, kept between runs — see [Take notes](#take-notes).
76
76
77
77
## Link to a tab
@@ -149,51 +149,57 @@ Since a `shark://` link names a window, a link written into a note stops working
149
149
— see above. Copy one for the tab you want to come back to *while you are writing about it*, and it will
150
150
take you there for as long as that window is open.
151
151
152
-
## Say what leaked
152
+
## The verdict
153
153
154
-
At the top of **What it is**, under the object's name, is the **Verdict** on it — `✗ Leaked`, `✓ Needed`, or
154
+
At the top of **What it is**, under the object's name, is the **Verdict** on it — `✗ Stuck`, `✓ Expected`, or
155
155
a quiet `? Unknown` — with the reason under it, in the same colours the chain on the left uses. Most objects
156
156
in a heap dump are `Unknown`, which is why that one is drawn small: the two that mean something are the ones
157
-
worth seeing across the room. This is the same answer a LeakCanary leak trace prints as `Leaking: YES`, `NO`
158
-
and `UNKNOWN`, said about the object rather than about the leak — an object *leaked*, and a leak is the
159
-
reference still holding it.
157
+
worth seeing across the room.
160
158
161
-
The reason is the whole of the answer, because half of these are about another object: an activity is red
162
-
because its own `mDestroyed` is true, and the view under it is red because the activity is. `Activity↑
163
-
leaked` is the chain saying so.
159
+
`Stuck` says the object should be gone and something is holding it. `Expected` says its being in memory is
160
+
legitimate at this point in the app's life. It is the same answer a LeakCanary leak trace prints as
161
+
`Leaking: YES`, `NO` and `UNKNOWN`, in words that stop short of calling the object the leak — because
162
+
**the leak is the faulty reference**, the one that should have been cleared, and everything under it is stuck
163
+
by that single mistake. An object nothing reaches any more is `Stuck` as well: it was expected to be gone,
164
+
and only the garbage collector not having run keeps it here. The verdict means the same thing everywhere.
164
165
165
-
**The pencil beside it** overrules it. Pick one of the three statuses, type why, and **Set the status**:
166
+
The reason is the rest of the answer, because half of these are about another object: an activity is red
167
+
because its own `mDestroyed` is true, and the view under it is red because the activity is. `Activity↑ is
168
+
stuck` is the chain saying so — and it is also how you find the faulty reference, which is the step between
169
+
the last `Expected` object and the first `Stuck` one.
170
+
171
+
**The pencil beside it** overrules the verdict. Pick one of the three, type why, and **Set the verdict**:
166
172
167
173
***Your answer wins**, whatever the inspectors said. Overruling is the point — an inspector reads a field,
168
174
you read the code, and a cache that is meant to hold what it holds is not something a field can say.
169
-
***The reason is required.** A status with no reason is one nobody who reads your heap dump next — a
170
-
colleague, an agent, you in a month — can check, and one of those makes every other status in it worth
175
+
***The reason is required.** A verdict with no reason is one nobody who reads your heap dump next — a
176
+
colleague, an agent, you in a month — can check, and one of those makes every other verdict in it worth
171
177
less. What you overruled is kept beside your reason rather than thrown away.
172
178
***It reads as yours**, wherever it appears: `set by hand — the cache is bounded, this is fine`, in the
173
179
panel and on every chain that runs through the object.
174
-
***Everything a leaked object holds has leaked too, and everything holding a needed one is needed too**, so
175
-
a verdict you set changes what the objects around it read as. Which is why setting one is usually enough to
176
-
make a whole chain make sense.
180
+
***Everything a stuck object holds is stuck too, and everything holding an expected one is expected too**,
181
+
so a verdict you set changes what the objects around it read as. Which is why setting one is usually enough
182
+
to make a whole chain make sense.
177
183
***The pencil again** on an object you have already decided about, and **Take it off** to hand it back to
178
184
the heap dump.
179
185
180
186
Because a verdict propagates along the chain, two of them can contradict each other: an object marked as
181
-
leaked, holding one marked as needed, cannot both be read off the chain between them. When what you are setting does that, **the window lists every status it disagrees with before writing
182
-
anything** — what the object is, which side of yours it is on, the reason it was given, and what it would
183
-
become. **Keep this and flip those** keeps yours and sets them to the opposite status, with what they said
184
-
kept as part of the new reason; **Undo** leaves the heap dump exactly as it was. Nothing is written until you
185
-
pick one.
187
+
stuck, holding one marked as expected, cannot both be read off the chain between them. When what you are
188
+
setting does that, **the window lists every verdict it disagrees with before writing anything** — what the
189
+
object is, which side of yours it is on, the reason it was given, and what it would become. **Keep this and
190
+
flip those** keeps yours and sets them to the opposite verdict, with what they said kept as part of the new
191
+
reason; **Undo** leaves the heap dump exactly as it was. Nothing is written until you pick one.
186
192
187
-
The statuses live in `~/.shark-explorer/leak-statuses`, one tab separated file per heap dump, with the columns
193
+
The verdicts live in `~/.shark-explorer/leak-statuses`, one tab separated file per heap dump, with the columns
188
194
named at the top — so they can be read, edited, diffed or pasted into an issue without this app, and they are
189
195
there again the next time you open that dump.
190
196
191
-
**The Leaks screen follows what you set**, because a status changes which objects are leaks and not only how
192
-
one of them reads: marking something as leaked makes it a leak, and whatever it holds stops being one — it is
193
-
only still in memory because of the object you named, and that is the thing to fix. Marking a leak as needed
194
-
takes it off the list. The one thing this costs is that a leak's
195
-
fingerprint matches the one LeakCanary reports only while nothing has been set by hand, since the fingerprint
196
-
is the stretch of chain your status has just moved.
197
+
**The Leaks screen follows what you set**, because a verdict changes which objects are leaks and not only how
198
+
one of them reads: marking something as stuck makes it a leak, and whatever it holds stops being one — it is
199
+
only still in memory because of the object you named, and that is the thing to fix. Marking a leak as
200
+
expected takes it off the list. The one thing this costs is that a leak's fingerprint matches the one
201
+
LeakCanary reports only while nothing has been set by hand, since the fingerprint is the stretch of chain your
0 commit comments