How ResourceCanary Automates Android Activity Leak and Bitmap Redundancy Detection
ResourceCanary, a Matrix sub‑module, automatically detects Activity memory leaks and redundant Bitmap objects in large‑scale Android apps by monitoring lifecycle callbacks, leveraging WeakReference queues, parsing Hprof dumps, and providing concise reference chains for fast debugging and performance optimization.
Background
As the WeChat Android client grew, manual code review could no longer keep up with the increasing risk of Activity leaks and redundant Bitmap objects, prompting the development of the ResourceCanary module to expose these issues and improve code quality.
Design Goals
Before automation, leak detection relied on memory‑usage thresholds, manual Hprof dumps, and MAT analysis, which required heavy human involvement. ResourceCanary aims to:
Automatically and accurately monitor Activity leaks and trigger Hprof dumps only when a leak is detected.
Automatically retrieve reference chains for leaked Activities and redundant Bitmaps.
Allow flexible extension of Hprof analysis logic and optional manual inspection.
Monitoring Stage
Detecting Activity Lifecycle End
The most reliable hook is Activity.onDestroy(). Two approaches were considered:
Make all Activities inherit a BaseActivity and record in its onDestroy().
Register a callback via Application.registerActivityLifecycleCallbacks() and record in onActivityDestroyed().
Because Matrix is meant to be non‑intrusive and Android 4.0‑ and earlier devices are rare, the second approach was chosen.
Detecting Garbage Collection
Since the VM does not expose a direct API to know when an object is collected, a WeakReference to the destroyed Activity is created. After forcing a GC, if the WeakReference becomes null and is enqueued, the leak is confirmed.
Redundant Bitmap detection is deferred to the analysis stage.
Analysis Stage
Extracting Reference Chains from Hprof
Hprof files contain all objects, classes, and reference relationships at dump time. By parsing the file into a graph and applying a shortest‑path search, the strong reference chain from a leaked Activity to a GC Root can be found.
GC Roots include classes, live threads, local variables, JNI references, and synchronized objects. The shortest chain is sufficient for developers to locate the problematic holder.
ResourceCanary reuses LeakCanary’s parsing library (named “haha”) and its reference‑chain extraction logic.
Finding Redundant Bitmaps
Android Monitor already provides a brute‑force method: extract the raw buffers of all unreclaimed Bitmaps, compare buffers of equal length to identify identical image data, and then apply the same reference‑chain algorithm to obtain the shortest path to a GC Root.
Making ResourceCanary Flexible
The monitoring step must run on the device, but the analysis step can be offloaded to a server. Splitting the workflow allows updating analysis logic without releasing a new client, storing Hprof files for manual review, and optionally enabling monitoring in production to catch hard‑to‑trigger leaks.
Details and Improvements
Reducing False Positives
Introduce a guaranteed‑to‑be‑collected sentinel object to confirm GC execution.
Use WeakReference.get() directly instead of waiting for the ReferenceQueue.
Require three consecutive failed GC checks and at least two subsequent Activity creations before reporting a leak.
Deduplicate leak reports by recording the Activity class name.
Trimming Hprof Files
Only string data and Bitmap buffers are needed for analysis; other buffers can be stripped, reducing file size by over 90%.
Improving Analysis Efficiency
The original LeakCanary algorithm searches for a single target. ResourceCanary modifies it to find shortest chains for multiple targets (e.g., many redundant Bitmaps) in a single traversal, avoiding repeated node visits.
Usage Effect and Overhead
Effectiveness
Example output shows a reference chain where SimpleLoginUI is held by ActivityThread.mOnPauseListeners, revealing the leak source.
Format: TypeName FieldName / array ElemType[] [index] android.app.ActivityThread mOnPauseListeners android.util.ArrayMap mArray array java.lang.Object[] [0] com.tencent.mm.plugin.account.ui.SimpleLoginUI instance
Performance Overhead
The monitoring runs in a background thread with a 1‑minute interval, causing an average frame‑rate drop of about 10 fps on UI‑heavy screens. Dumping Hprof freezes the app for 5‑15 seconds, which is acceptable because the feature is disabled in production. Pre‑processing the Hprof takes 3‑20 seconds and uses ~1.5× its size in memory. Server‑side analysis of a 200 MB dump averages 15 seconds.
Future Plans
ResourceCanary will continue to reduce false positives, improve reliability, and handle ROM fragmentation issues.
About Matrix
Matrix is open‑source at https://github.com/Tencent/matrix . Contributions are welcome.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
WeChat Client Technology Team
Official account of the WeChat mobile client development team, sharing development experience, cutting‑edge tech, and little‑known stories across Android, iOS, macOS, Windows Phone, and Windows.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
