How to Inspect App Memory Allocation and Native Leaks on Android Using ADB dumpsys meminfo

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.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.