Memory management in Android relies on an aggressive Linux kernel out-of-memory (OOM) killer combined with the Android Runtime (ART) garbage collector. When an application consumes excessive RAM, retains unused object references, or leaks native allocations in C++ libraries, ART garbage collection cannot free the space, eventually triggering an OutOfMemoryError or causing the system OOM killer to terminate the process. To diagnose memory consumption at a granular level\u2014differentiating Dalvik heap, native allocations, graphics buffers, and unreachable memory\u2014the Android Debug Bridge (ADB) provides the diagnostic command dumpsys meminfo.
Understanding Proportional Set Size (PSS) and Memory Metrics
Traditional Linux tools report resident set size (RSS), which includes shared libraries counted multiple times across every process that loads them. Android evaluates memory using Proportional Set Size (PSS) and Private Dirty RAM:
- PSS (Proportional Set Size): Measures the application\u2019s total memory footprint, accounting for shared pages proportionally divided among all sharing processes.
- Private Dirty: RAM allocated exclusively to the app that has been modified and cannot be paged out to disk (e.g., app-created objects, bitmaps, and native heap allocations). This is the true memory cost of an application.
- Private Clean: RAM mapped to read-only files (such as APK dex code and assets) that the kernel can reclaim without saving when under memory pressure.
- Swap / ZRAM: Memory compressed and moved to virtual swap space to relieve physical RAM constraints.
Inspecting Total Device Memory and Top Consumers
To inspect overall device memory utilisation, total RAM, and find the top memory-consuming processes across the entire Android system:
adb shell dumpsys meminfo
The command lists memory consumption grouped by process state (native, persistent foreground, visible, perceptor, previous, and cached). At the bottom of the summary, Android reports physical RAM usage:
Total RAM: 7,854,320K (status normal)
Free RAM: 3,412,180K ( 542,120K cached pss + 2,120,400K cached kernel + 749,660K free)
Used RAM: 3,842,140K (3,124,500K used pss + 717,640K kernel)
Lost RAM: 600,000K
ZRAM: 342,100K physical used for 1,120,500K in swap (2,097,148K total swap)
Inspecting Memory Allocation for a Specific Package
To inspect a single application in detail, pass the package name or process ID (PID) to dumpsys meminfo:
adb shell dumpsys meminfo com.example.myapp
The command outputs a comprehensive matrix categorising allocations:
** MEMINFO in pid 18452 [com.example.myapp] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 34120 33980 0 210 65536 31200 34336
Dalvik Heap 24500 24210 0 120 48128 23100 25028
Dalvik Other 3410 3200 0 0
Stack 1240 1240 0 0
Ashmem 412 400 0 0
Gfx dev 14200 14200 0 0
Other dev 48 0 48 0
.so mmap 8450 820 5200 40
.apk mmap 2100 0 1840 0
.dex mmap 4500 4 4120 0
TOTAL PSS: 92980 78054 11208 370 113664 54300 59364
Key sections to monitor:
- Native Heap: Memory allocated by C/C++ libraries (such as image decoders, game engines, or NDK code) using
malloc. High native heap values indicate unmanaged buffer accumulation. - Dalvik Heap: Java and Kotlin object allocations managed by the ART runtime.
- Gfx dev: GPU memory buffers used for textures, surface views, and rendering pipelines.
- .so / .dex mmap: Memory-mapped shared libraries and bytecode loaded by the classloader.
Detecting Unreachable Memory and Native Leaks
Standard Java memory analysers often miss native memory leaks originating in the NDK or JNI layer. The Android memory subsystem includes a built-in unreachable memory detector accessible via the --unreachable flag:
adb shell dumpsys meminfo --unreachable com.example.myapp
This command pauses the target process briefly, traverses active pointers across the native heap and stack, and identifies allocated native blocks that have no valid references pointing to them:
Unreachable memory
128 bytes unreachable at 0x7c458120
referenced by nothing
contents:
0x7c458120: 00 00 00 00 48 65 6c 6c 6f 20 57 6f 72 6c 64 00 ....Hello World.
256 bytes unreachable at 0x7c459300
referenced by nothing
contents:
0x7c459300: 28 34 00 00 00 00 00 00 12 00 00 00 00 00 00 00 (4..............
Total unreachable memory: 384 bytes in 2 allocations
If unreachable memory is reported, native code has allocated memory that was never released and can no longer be reached by the application.
Automating Continuous Memory Logging
To monitor memory leaks over extended testing sessions (such as repetitive activity transitions), automate the extraction of PSS and Private Dirty numbers using a bash loop:
while true; do
adb shell dumpsys meminfo com.example.myapp | grep -E "TOTAL PSS|Native Heap|Dalvik Heap"
sleep 5
done
If TOTAL PSS or Native Heap continuously escalates without dropping after garbage collection cycles, the application is suffering from memory retention or leak issues.