AOSP Expert & Production Engineering
4 min read

Memory Leaks & Deadlocks

Systematic Memory Leak Investigation Workflow

Memory leaks in Android, particularly at the framework and system levels, can cause profound performance degradation and ultimately system crashes (Out-Of-Memory, OOM). A leak occurs when objects are no longer needed but remain referenced, preventing the Garbage Collector (GC) from reclaiming their memory.

1. Detection and Verification

The first step is confirming a leak exists. Continuous monitoring of memory usage is crucial.

# View summary of memory usage for a specific process
adb shell dumpsys meminfo <process_name>

Observe the Dalvik Heap and Native Heap allocations over time. If they steadily climb without dropping after GC cycles or when returning to an idle state, a leak is highly probable.

2. Capturing a Heap Dump

To analyze what is leaking, you must capture a heap dump (an hprof file).

# Trigger a heap dump for a process
adb shell am dumpheap <process_name> /data/local/tmp/heapdump.hprof

# Pull the file to your host machine
adb pull /data/local/tmp/heapdump.hprof .

3. Analysis with MAT or Android Studio

Open the hprof file using the Android Studio Memory Profiler or the Eclipse Memory Analyzer Tool (MAT).

  • Dominator Tree: Identifies the largest objects retaining the most memory.
  • Histogram: Sorts objects by class and instance count. Look for exceptionally high counts of specific classes (e.g., Activity, View, or custom data structures).
  • GC Roots: The most critical step is tracing the reference chain from the leaked object back to a GC Root (an object the system considers always active, like static variables or active thread locals). This path reveals why the object is not being garbage collected.

Deadlock Detection via Thread Dump Analysis

A deadlock occurs when two or more threads are blocked indefinitely, each waiting for a lock held by another thread in the group.

Capturing a Thread Dump

When a system process (like system_server) deadlocks, the device often becomes unresponsive. The primary tool for diagnosing this is the ANR (Application Not Responding) trace or a manual thread dump.

# Trigger a thread dump (SIGQUIT) for a process
adb shell kill -3 <pid>

The resulting dump is typically written to /data/anr/traces.txt.

Analyzing the Trace

Examine the thread states. Look for threads in the Blocked or Waiting state. The trace will often explicitly identify the lock the thread is waiting for and which thread currently holds it.

"Thread-1" prio=5 tid=12 Blocked
  - waiting to lock <0x01234567> (a java.lang.Object) held by thread 14
  at com.example.MyClass.methodA(MyClass.java:42)

"Thread-2" prio=5 tid=14 Blocked
  - waiting to lock <0x089abcdef> (a java.lang.Object) held by thread 12
  at com.example.MyClass.methodB(MyClass.java:84)

This classic example shows a circular dependency.

Binder Deadlock Patterns

Binder is Android's IPC mechanism. Deadlocks involving Binder are common and complex because they span process boundaries.

A typical pattern is a synchronous callback deadlock:

  1. Process A calls a synchronous Binder method on Process B, holding Lock1.
  2. Process B, while handling the request, calls a synchronous Binder method back to Process A.
  3. The incoming request to Process A attempts to acquire Lock1, which is already held by the thread waiting for Process B to respond.

Detection: Look for threads stuck in binder_thread_read or IPCThreadState::waitForResponse. The thread dump will show a thread waiting on a Binder transaction while holding a local lock.

Lock Ordering and Prevention

The most robust strategy to prevent deadlocks is enforcing a strict Lock Ordering hierarchy.

If multiple locks must be acquired, they must always be acquired in the exact same global order across all threads.

// Correct: Always acquire lockA before lockB
synchronized(lockA) {
    synchronized(lockB) {
        // Critical section
    }
}

If another thread attempts to acquire lockB then lockA, a deadlock risk is introduced. Documentation and rigorous code review are essential to maintain lock ordering, especially in complex system services.