Identify a giant array¶
The problem¶
The summary shows one char[] of 544 KiB, or a byte[] of 154 KiB, near the top of the largest instances. The class name says nothing about
what is in it, and a different content needs a different fix: a cached JSON response, an image, a log buffer, or a block of zeros.
The solution¶
-
Take the array's object id from the summary and preview it. This sample dump has a
char[], anint[]and abyte[]near the top:Largest array instances object ids (for retainer tracing): 544.01KiB object_id=1723142144 char[] content: JSON {"0019187198e2cd05a629f3ee787deb7c8b44dd45ab66e9559b94a9f397f16fc2":"https://artworks.thetvdb.com/ba... 418.64KiB object_id=2595270656 int[] content: unknown binary, 428688 bytes total 00000000 00 00 00 cb 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 154.16KiB object_id=1740406800 byte[] content: JSON {"0019187198e2cd05a629f3ee787deb7c8b44dd45ab66e9559b94a9f397f16fc2":"https://artworks.thetvdb.com/banners/episodes/76733/213300.jpg","01a3fe... -
Read the
content:label. Thechar[]and thebyte[]are JSON, and theint[]isunknown binary. -
Find out who holds the array you care about. A
Stringwraps mostchar[]s, so ask for the chain:Path from object_id=1723142144 (depth 3 step(s)): start ── id=1723142144 content: JSON {"0019187198e2cd05a629f3ee787deb7c8b44dd45ab66e955... hop 1 ── id=337740168 (via java.lang.StringBuffer.value) hop 2 ── id=337740144 (via java.io.StringWriter.buf) hop 3 ── id=337740240 (via com.google.gson.stream.JsonWriter.out) → orphan: no holder found in dump -
Read the chain from the array up: a
StringBuffer, inside aStringWriter, inside a GsonJsonWriter. These are the objects that build JSON text. The array is the output buffer of a serializer that was run on a large object. That is already a diagnosis the class name alone could not give.
How it works¶
The summary tells you that an array is big, the preview tells you what is in it, and the path tells you who owns it. Together they replace a search through the source for whoever builds such a string.
Match the label to a likely cause:
| Label | Often means | Look at |
|---|---|---|
JSON |
A cached or serialized response, or a serializer's output buffer. | Who builds the string: the path usually ends in a writer or a cache map. |
XML |
A preferences file, a layout or a config loaded whole. | SharedPreferencesImpl.mMap on Android. |
PNG image, JPEG image, … |
An encoded image held as bytes. | An image cache or a network layer buffering a body. |
binary/repeated-fill |
A buffer that was allocated and never filled. | A pool or buffer sized for a worst case. |
unknown binary |
A packed structure, a native handle table, an index. | The holder's class name. |
Variations¶
- The path ends in
orphan. The walker does not follow class static fields. Ask--find-referrers id:<last id>about the last hop. See Paths to a GC root. - There are many arrays of the same size. Do not trace them one by one. See Find duplicate payloads.
- You want the size of what the holder retains. Add
--retained-sizeto the path command. - The id is a
String's array and you want the string. The preview is the string. Nothing else is needed.
Related¶
- Content previews lists every label and how arrays are decoded.
- Object ids explains where ids come from.
- Paths to a GC root shows how to read the chain.