Posts

Showing posts from June, 2026

🗂️ Heap Dumping Explained - LeakCanary's Bold Move

Image
  Introduction In Part 2, we explored ObjectWatcher , the silent guardian that tracks destroyed objects. But what happens when ObjectWatcher suspects foul play? LeakCanary doesn’t just shrug — it goes nuclear: it dumps the heap . Heap dumping is the most dramatic step in LeakCanary’s workflow. It freezes your app, writes a snapshot of memory to disk, and hands it over to Shark for analysis. Let’s break down why, how, and what happens next. ⚙️ What is a Heap Dump? A heap dump is a binary snapshot of the JVM heap at a given moment. It contains: All objects currently in memory. Their classes and fields. References between objects. GC roots (entry points into the object graph). Think of it as a crime scene photo : everything in memory is captured exactly as it is. 🧑‍💻 Triggering a Heap Dump LeakCanary triggers a heap dump when retained objects exceed a threshold: 5 retained objects when the app is visible. 1 retained object when the app is backgrounded. Code Example HeapDumpTrigg...

🦈 Shark Heap Analysis - LeakCanary's Detective at Work

Image
  Introduction In Part 3, we saw how LeakCanary boldly freezes your app to capture a heap dump . But a .hprof file is just raw binary data — unreadable to humans. Enter Shark , LeakCanary’s analysis engine. Shark parses the dump, builds a navigable object graph, and uncovers the exact reference chains that keep your objects alive. This is where leaks stop being mysterious and start becoming actionable. ⚙️ Shark’s Workflow 1. Parsing the Heap Dump Shark uses Shark Hprof (built on Okio) to stream through .hprof records efficiently. val heapGraph = Hprof.openHeapGraph(heapDumpFile) Converts raw data into a HeapGraph . HeapGraph exposes classes, objects, fields, and references. 2. Building the Heap Graph HeapGraph lets you query memory like a database: val activityClass = heapGraph.findClassByName( "com.example.LeakyActivity" ) val instances = activityClass.instances You can traverse references between objects. This graph is the foundation for leak detection. 3. Findi...

🌳 Dominator Tree & Retained Size - Measuring the True Cost of Memory Leaks

Image
  Introduction In Part 4, we saw how Shark parses heap dumps and generates leak traces. But not all leaks are equal. A retained Activity holding onto dozens of Views is far more dangerous than a single leaked Bitmap . To prioritize, Shark calculates retained size using a dominator tree algorithm . This lets developers see not just what is leaking, but how much memory is being wasted. 🧩 What is a Dominator Tree? A dominator tree is a graph structure used in memory analysis: Each node represents an object in the heap. A node dominates another if every path from a GC root to that object passes through the dominator. The retained size of a node is the total memory that would be freed if that node were garbage collected. Think of it like a family tree of memory ownership : if the parent dies, all children go with it. ⚙️ Shark’s Retained Size Calculation 1. Build Heap Graph val heapGraph = Hprof.openHeapGraph(heapDumpFile) 2. Construct Dominator Tree val dominatorTree = Dom...

🧩 Common Android Leak Patterns - Real‑World Traps LeakCanary Catches

Image
  Introduction In Part 5, we explored how Shark quantifies leaks with dominator trees and retained size . But theory only gets us so far. Let’s look at the real‑world leak patterns that plague Android apps — the ones LeakCanary was built to catch. These examples are code‑driven, diagram‑supported, and exactly the kind of content that sparks discussion in the Android community. 1. Static Context Reference object ContextHolder { var context: Context? = null } ❌ Holding a Context in a static field prevents GC. If it’s an Activity context, the entire UI tree leaks. Diagram: GC Root → ContextHolder → Activity → Views 2. Anonymous Inner Class Leak class LeakyActivity : AppCompatActivity () { private val runnable = Runnable { // references outer Activity } } Inner classes hold implicit references to their outer class. If posted to a long‑lived thread, the Activity leaks. 3. Handler Leak class LeakyActivity : AppCompatActivity () { private val hand...

⚡ Integrating LeakCanary in CI/CD - Automating Leak Detection

Image
  Introduction So far, we’ve explored how LeakCanary detects leaks, analyzes heap dumps with Shark, and categorizes them. But in real teams, leaks often slip through because developers only check them locally. The next step is automation : integrating LeakCanary into your CI/CD pipeline so leaks are caught before release. This transforms LeakCanary from a developer tool into a team safeguard . 🛠️ Configuring LeakCanary for CI LeakCanary is designed for debug builds , but you can configure it to export leak reports for CI. Gradle Setup dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-android:2.12" } Exporting Leak Reports LeakCanary stores reports in: /storage/emulated/0/Download/leakcanary/ You can configure CI to collect these reports after instrumentation tests. 🧑‍💻 Example: CI Script # Run instrumentation tests ./gradlew connectedDebugAndroidTest # Collect LeakCanary reports adb pull /storage/emulated/0/Download/leakcanary ./ci-reports/leaks ...

📢 LeakCanary Reporting & Advocacy - Turning Leak Traces into Stories

Image
  Introduction LeakCanary doesn’t just detect leaks — it reports them in a way that developers can act on. But the real power lies in how you, as an engineer and advocate, interpret those reports and communicate their impact. This is the difference between “I fixed a leak” and “I helped my team understand why memory leaks degrade UX and how to prevent them.” That’s advocacy. 🧩 Anatomy of a Leak Report LeakCanary surfaces leaks via: In‑app notifications (quick feedback loop). Detailed LeakTrace UI (reference chains). Exported reports (for CI/CD integration). Example LeakTrace ┬─── │ GC Root: System class │ ├─ android.view.inputmethod.InputMethodManager │ Leaking: NO │ ↓ InputMethodManager.mLastSrvView ├─ com.example.LeakyActivity │ Leaking: YES (Activity was destroyed) │ Retained size: 5 MB This tells you: The GC root holding the leak. The chain of references. Whether the object is leaking. The retained size impact. 📊 Diagram: Leak Report Flow Heap...

LeakCanary ObjectWatcher Deep Dive - The Silent Guardian of Memory

Image
  Introduction In Part 1, we saw how LeakCanary detects leaks at a high level. But the real magic begins with ObjectWatcher  — the component that quietly observes destroyed objects and decides whether they’re truly gone or suspiciously retained. Understanding ObjectWatcher is crucial if you want to debug leaks beyond surface level and explain them with authority. 🧩 What is ObjectWatcher? ObjectWatcher is a core utility inside LeakCanary that: Tracks destroyed objects (Activities, Fragments, Views, ViewModels). Holds them via weak references . Periodically checks if they’ve been garbage collected. Flags them as retained if they’re still alive after GC. Think of it as a security guard : once an object leaves the building (destroyed), ObjectWatcher ensures it doesn’t sneak back in. ⚙️ How ObjectWatcher Works 1. Watching Objects objectWatcher.watch( destroyedActivity, "Activity was destroyed but not GC'd" ) LeakCanary calls watch() whenever a lifecycle event signa...

🚨 Why Memory Leaks Matter in Android (and How LeakCanary Saves You)

Image
  Every Android engineer has faced the dreaded OutOfMemoryError . It doesn’t happen on day one, but weeks after release, when users have navigated through dozens of screens. Suddenly, the app slows down, GC thrashes, and boom — crash. The silent culprit? Memory leaks. This post kicks off a series where we’ll dissect LeakCanary and its engine Shark , showing you not just how to use them, but how they work internally. By the end, you’ll be able to debug leaks like a pro — and explain them like an advocate. 🧠 What is a Memory Leak? A memory leak occurs when an object that should be garbage collected is still retained in memory because something is holding a reference to it. Example: Leaking Activity class LeakyActivity : AppCompatActivity () { companion object { // ❌ Static reference keeps Activity alive var instance: LeakyActivity? = null } override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) ...

LeakCanary Internals — A Complete Guide for Android Engineers

Image
Introduction Over the past few weeks, we’ve gone on a journey through LeakCanary and its engine Shark — from the basics of memory leaks to the deepest internals of heap analysis. This wrap‑up post ties everything together, giving you a single reference point for the entire series. If you’re serious about Android performance, debugging, and advocacy, this is your roadmap. 📚 The Series in Review 1. Why Memory Leaks Matter Defined leaks and their dangers. Showed a leaking Activity example. Explained why leaks silently degrade performance. 2. ObjectWatcher Deep Dive Explored how LeakCanary tracks destroyed objects. Weak references + ReferenceQueue checks. Manual watching for custom objects. 3. Heap Dumping Explaine How LeakCanary freezes the app and writes .hprof . Trade‑offs of heap dumping. Why it’s worth it for accuracy. 4. Shark Heap Analysis Parsing .hprof into HeapGraph. Shortest path finder from GC roots. LeakTrace reports with actionable insights. 5. Dominator Tree & Retai...