🦈 Shark Heap Analysis - LeakCanary's Detective at Work

Shark Heap Analysis — How LeakCanary Turns Heap Dumps into Actionable Leak Traces

Learn how Shark parses Android heap dumps, builds a HeapGraph, traces GC-root paths, filters known leaks, and generates actionable LeakCanary reports.

In the previous article, we saw how LeakCanary freezes the app briefly to capture a .hprof heap dump. But a heap dump on its own is only raw binary data. To make it useful, LeakCanary needs an analysis engine that can parse memory records, reconstruct object relationships, and explain why retained objects are still alive.

That analysis engine is Shark.

Shark is where memory leak debugging becomes understandable. It transforms a raw heap snapshot into a structured object graph, finds the most relevant retaining paths, and produces the LeakTrace reports developers use to fix leaks.

Table of Contents

1. What Is Shark?

Shark is LeakCanary’s heap analysis engine. Its purpose is to inspect the .hprof file generated after a heap dump and convert that low-level memory snapshot into something developers can reason about.

In practice, Shark parses raw heap records, builds a graph of objects and references, locates suspected leaking objects, and traces how those objects remain reachable from GC roots.

2. Why Shark Matters

Without Shark, a heap dump would remain an unreadable artifact. Shark is the layer that turns memory data into debugging insight.

  • It parses raw heap data into a queryable object graph.
  • It automates leak detection by finding shortest or most relevant retention paths.
  • It filters known patterns using reference matching for framework or library leaks.
  • It produces LeakTrace output that developers can use directly.

This is why Shark is often the difference between “we have a heap dump” and “we know what to fix.”

3. Parsing the Heap Dump

Shark begins by reading the .hprof heap dump format. In the original article, this is described as Shark Hprof streaming through heap records efficiently and opening the dump as a graph-backed structure. [page:1]

val heapGraph = Hprof.openHeapGraph(heapDumpFile)

This step is important because it converts raw binary memory data into an internal model that Shark can traverse and analyze.

4. Building the HeapGraph

Once the dump is parsed, Shark exposes the memory snapshot as a HeapGraph. This graph contains classes, object instances, fields, and references between objects.

val activityClass = heapGraph.findClassByName("com.example.LeakyActivity")
val instances = activityClass.instances

A good mental model is to think of HeapGraph like a memory database. Instead of guessing what is retaining an object, Shark can traverse the reference graph directly.

5. How Shark Finds Leaks

Shark’s HeapAnalyzer coordinates the core leak detection flow. It starts from suspected leaking objects, then works backward through the heap graph to understand why those objects are still reachable.

  1. Identify suspected leaking objects, often provided by ObjectWatcher.
  2. Find shortest paths from GC roots to those objects.
  3. Apply ReferenceMatchers to filter or classify known library and framework leaks.
  4. Generate a human-readable LeakTrace.

This is the stage where Shark transforms object retention into a causal explanation.

6. Reading a LeakTrace Example

A LeakTrace is easiest to understand when you read it from the GC root down to the leaking object:

GC Root: System class
↓
android.view.inputmethod.InputMethodManager
↓ InputMethodManager.mLastSrvView
com.example.LeakyActivity

Leaking: YES (Activity was destroyed)

This tells you that the destroyed Activity is still reachable because a system-managed object, InputMethodManager, is holding a reference chain to it.

That is what makes Shark useful: it does not just say “this object leaked.” It shows the actual path that kept it alive.

7. Retained Size and Prioritization

Shark does more than find retention paths. It also uses dominator tree analysis to estimate retained size, which helps teams understand how much memory would be recovered if a leaking object were fixed. [page:1]

  • Small retained size may indicate a minor leak.
  • Large retained size often points to a high-priority memory problem.
  • Retained size helps teams prioritize fixes instead of treating every leak as equally severe.

This is why Shark is both a debugging engine and a prioritization engine.

8. Shark Analysis Flow

The high-level flow can be summarized like this:

Heap Dump (.hprof) → Shark Hprof → HeapGraph → HeapAnalyzer → LeakTrace → Developer Fix

That pipeline is where raw memory inspection becomes engineering action.

9. Analyze LeakCanary Reports Faster with LeakLens

Shark gives you the core analysis, but complex leak traces can still take time to interpret. LeakLens complements this workflow by analyzing LeakCanary reports, explaining retention paths, and suggesting likely fixes directly inside Android Studio.

It is especially useful once you already understand Shark’s role in the leak analysis pipeline.

Explore LeakLens

10. What to Read Next

This article covers the analysis engine behind LeakCanary. To continue through the full series, read the related posts below:

FAQ

What is Shark in LeakCanary?

Shark is LeakCanary’s heap analysis engine. It parses heap dumps, builds object graphs, finds retaining paths, and generates LeakTrace reports.

What is a HeapGraph in Shark?

HeapGraph is Shark’s graph representation of the heap dump, exposing classes, instances, fields, and references for memory analysis.

How does Shark find memory leaks?

Shark starts from suspected leaking objects, traces paths from GC roots, applies reference matchers, and generates human-readable leak traces.

This guide is part of the Android Quality Suite.

Comments

Featured Articles

Optimize Jetpack Compose: Performance & Best Practices

Play Store Uploads with Fastlane Supply - 4

Building a Production-Ready Kotlin Multiplatform Platform Kit: Lessons from My BlrKotlin x InMobi Talk

Android Device Security: Sandboxing, Rooting, and Attestation Explained

From ‘Master’ to ‘Main’: The Meaning Behind Git’s Naming Shift