Why Memory Leak Detection Shouldn't Run on Your Device
How I built LeakLens to move heap analysis from Android apps into Android Studio.
You’re deep in a refactor when your test device lights up with a familiar notification:
4 retained objects, dumping heap.
You pick up the phone, inspect the leak trace, try to remember the class names, and then jump back to Android Studio to find the source.
The bug report is useful. The interruption isn’t.
That experience led me to ask a simple question:
Why is the tool that finds my memory leaks running inside the application I’m trying to debug?
That question became LeakLens, an Android Studio plugin that performs memory leak analysis directly inside the IDE, without requiring an SDK in the application itself.
The Problem: Leak Detection Comes With a Cost
For years, tools like LeakCanary have been the gold standard for Android memory leak detection. They work exceptionally well, but they also require a runtime dependency, consume device resources, and introduce an extra debugging workflow.
When a leak is detected, the investigation often starts on the device and ends in the IDE. That constant back-and-forth creates friction, especially when you’re already focused on solving a different problem.
I wanted a workflow that:
- Doesn’t require another SDK dependency.
- Doesn’t consume application resources for analysis.
- Doesn’t force developers to leave their IDE to investigate leaks.
In short, I wanted leak detection to live where developers already spend their time: inside Android Studio.
Moving Heap Analysis to the Workstation
Instead of embedding analysis inside the application process, LeakLens offloads the heavy lifting to the developer machine.
Using ADB, ddmlib, and Android SDK tooling, the plugin can:
- Trigger a native heap dump.
- Pull the generated
.hproffile from the device. - Analyze it locally using Shark, the same heap analysis engine that powers LeakCanary.
This changes the workflow completely.
Rather than asking a mobile device with limited CPU and memory to analyze a potentially large heap dump, the analysis runs on a workstation with significantly more resources available.
The application remains lightweight, while the IDE performs the expensive work.

The Turning Point: Combining Prevention With Detection
Runtime analysis is powerful, but it’s also reactive.
To discover a leak, you first need to run the application, navigate to the right screen, trigger the problematic code path, and generate a heap dump.
The more I worked on LeakLens, the more obvious another opportunity became:
Many memory leaks follow patterns that are detectable long before the application ever runs.
This led to the idea of a dual-layer approach.
Layer 1: Static Analysis with UAST
The first layer focuses on prevention.
Using UAST (Universal Abstract Syntax Tree), LeakLens analyzes source code directly inside the editor and identifies common memory leak patterns in both Kotlin and Java.
Examples include:
- Activity references stored in static fields.
- Context objects retained in singleton instances.
- Lifecycle-aware components used incorrectly.
- Common listener and callback retention patterns.
Instead of waiting for a heap dump, LeakLens highlights the issue while you’re writing code.
The goal is simple:
Catch obvious leaks before they ever reach a device.

Layer 2: Runtime Heap Analysis
Not every leak can be discovered statically.
Complex object graphs, third-party libraries, asynchronous callbacks, and framework interactions often require runtime inspection.
For those situations, LeakLens uses Shark-powered heap analysis to provide:
- Retained object reports.
- Leak traces.
- Reference chains.
- Retained size calculations.
- Source navigation directly from the IDE.
By combining static inspections and runtime analysis in a single tool window, developers get a unified view of application memory health.
An Unexpected Challenge: When Kotlin Became the Problem
One of the most interesting engineering challenges had nothing to do with heap dumps.
It came from the IntelliJ Plugin Verifier.
My original implementation followed a fairly standard Kotlin-first approach. The plugin worked correctly, but verification against newer IntelliJ platform releases started producing a surprising number of compatibility violations.
After several rounds of investigation, the issue turned out to be Kotlin-generated bridge methods.
Some IntelliJ interfaces include default members such as methods related to icons, anchors, and extension point metadata. In newer platform versions, several of these APIs are marked as internal.
The plugin wasn’t calling those APIs directly.
However, Kotlin-generated bridge methods were.
From the verifier’s perspective, the plugin was touching internal APIs and therefore failing compatibility checks.
The solution was unexpectedly simple:
I moved several core extension-point implementations back to Java.
By using a small Java-Kotlin hybrid architecture, I was able to avoid the generated bridge-method issue entirely and restore compatibility across supported IDE versions.
It was a valuable reminder that the most modern solution is not always the most stable one, especially when building against large platform APIs.
Turning Leak Reports Into Actionable Fixes
Finding a leak is only part of the problem.
Understanding why it happened is usually the harder part.
Framework leaks, listener chains, and lifecycle interactions can produce reference graphs that are difficult to interpret, even for experienced developers.
To help with this, LeakLens includes an AI-assisted analysis workflow.
Rather than sending a raw leak trace to an LLM, the plugin builds a structured request that includes:
- The reference chain.
- Retained object information.
- Retained size metrics.
- Relevant source code context.
Providing the model with the reasoning path behind the leak significantly improves the quality of the suggestions it generates.
The result is more than generic advice — it often produces actionable explanations and code-level fixes.
Making Leak Detection Practical for Large Codebases
One of the most common concerns I heard from experienced developers was noise.
Imagine introducing a memory analysis tool into a project that already contains hundreds of existing leaks.
Finding leak number 501 is not useful if the first 500 are already known.
To address this, LeakLens includes a VCS-friendly baseline system.
Teams can generate a baseline file, commit it to source control, and treat existing issues as known technical debt.
From that point forward, the plugin only highlights newly introduced leaks and regressions.
This shifts memory management from a cleanup task to a development guardrail.
Instead of being overwhelmed by historical issues, teams can focus on preventing new ones from entering the codebase.
Conclusion
Memory leaks are rarely caused by a lack of tools.
More often, they’re caused by friction.
Every context switch, every manual heap dump investigation, and every issue deferred until later increases the likelihood that a leak survives into production.
LeakLens was built to reduce that friction by bringing memory analysis back into the IDE.
Static inspections help prevent common mistakes before code reaches a device. Host-side heap analysis provides the depth needed to investigate complex runtime leaks. Together, they make memory health part of the development workflow rather than an occasional debugging exercise.
If you’re interested in trying it, LeakLens is open source and available on the JetBrains Marketplace.
Useful Links
- Install LeakLens: https://plugins.jetbrains.com/plugin/32079-leaklens/
- Source Code: https://github.com/dev-vikas-soni/leak-lens

Comments
Post a Comment