In mobile systems architecture, managing multitasking across constrained physical RAM requires constant, sophisticated process lifecycle management. When physical memory becomes exhausted on an Android device, the Linux kernel and Android’s Low Memory Killer Daemon (LMKD) must make immediate, automated decisions about which background processes to terminate to prevent user-facing stutter or system crashes. To prioritize which applications survive memory pressure, the Android framework assigns every running process a dynamic OOM (Out-of-Memory) Adjustment score (oom_adj) and a formal Process State (ProcState). For Android developers investigating sudden background app deaths, service restarts, or ANR (Application Not Responding) timeouts, understanding these scoring metrics is essential. Using the Android Debug Bridge (ADB), engineers can inspect live process state hierarchies and OOM score distributions via dumpsys activity processes.
Understanding Process Lifecycle States and OOM Adjustment Scores
The Android ActivityManagerService continuously monitors active user interactions, foreground activities, bound services, content providers, and audio streams, calculating an integer OOM adjustment score ranging from -1000 to 1000:
- Native and System Processes (-1000 to -700): Core system services (such as
init,vold,surfaceflinger, andsystem_server). These processes are completely protected against kernel eviction. - Persistent and System UI (-800 to -600): Privileged packages declared persistent, such as telephony and the Android System UI.
- Foreground Activities (0): The active application currently rendered on the user’s screen with which the user is interacting.
- Visible Processes (100): Applications hosting visible activities that are partially obscured (e.g., behind a dialog) or bound to foreground services with user-visible notifications.
- Perceptible Processes (200): Apps playing background audio, recording microphone data, or providing accessibility services.
- Service Processes (500): Background services performing network synchronization or media playback without an active foreground notification.
- Cached Processes (900 to 1000): Applications kept dormant in memory to allow rapid re-launch. These are the first targets terminated when free memory dips below minimum watermarks.
The dumpsys activity processes command outputs the exact real-time ordering and score assignments calculated by the system.
Connecting via ADB and Verifying Shell Access
Ensure that your target Android device has Developer Options and USB Debugging enabled. Connect the device via USB or wireless ADB, and confirm that the ADB daemon recognizes the device:
adb devices
Verify that your device serial is displayed with the device status before issuing shell diagnostic commands.
Querying the Complete Process State Dump
To inspect the comprehensive process ranking table generated by ActivityManagerService, execute the following command:
adb shell dumpsys activity processes
The resulting output contains detailed diagnostic sections, including the LRU (Least Recently Used) process list, active process records, and current OOM adjustment tables:
ACTIVITY MANAGER PROCESSES (dumpsys activity processes)
-------------------------------------------------------------------------------
Process LRU list (sorted by oom_adj, 52 total):
#51: adj=0/F proc=ProcessRecord{a1b2c3d 2104:com.android.chrome/u0a120}
#50: adj=100/V proc=ProcessRecord{e4f5a6b 3452:com.spotify.music/u0a85}
#49: adj=200/P proc=ProcessRecord{c7d8e9f 4102:com.google.android.apps.maps/u0a94}
#48: adj=500/S proc=ProcessRecord{1a2b3c4 5218:com.example.cloudsync/u0a142}
#12: adj=905/C proc=ProcessRecord{5d6e7f8 8910:com.twitter.android/u0a160}
#01: adj=995/C proc=ProcessRecord{9a0b1c2 9450:com.instagram.android/u0a175}
Filtering Process States by Target Application
Because querying all system processes outputs thousands of lines of diagnostic text, you can isolate metrics for a single target package using grep:
adb shell dumpsys activity processes | grep -A 8 -B 2 "com.example.myapp"
Key parameters reported in the process record include:
- adj: The calculated numeric OOM adjustment score (e.g.,
0for foreground,905for cached). - state: The symbolic Process State letter (e.g.,
Ffor Foreground,Vfor Visible,Pfor Perceptible,Sfor Service,Cfor Cached). - curSchedGroup: The CPU scheduling group assigned by the kernel (e.g.,
fgfor foreground CPU cgroups,bgfor throttled background cgroups). - hasForegroundServices: Boolean indicating whether the process holds an active
ForegroundServicetoken.
Inspecting Process State Transitions During Lifecycle Events
To evaluate whether your application handles lifecycle transitions properly without prematurely dropping into cached kill tiers, monitor process state changes dynamically.
Launch your application on the device and query its OOM adjustment:
adb shell dumpsys activity processes | grep "com.example.myapp" | grep "adj="
Observe that while on-screen, the app reports adj=0 (Foreground). Press the Home button to send the application to the background. Immediately re-query the command: the score transitions to adj=700 (Previous app) and then settles into adj=900+ (Cached app).
If your application starts an active background foreground service (such as audio recording or navigation), the score should transition to adj=100 or adj=200. If it remains at adj=900, your service failed to bind properly or lacked the necessary foreground service type declaration.
Auditing Low Memory Killer Minimum Free Memory Watermarks
To understand the memory thresholds that trigger the termination of cached processes, inspect the kernel memory watermarks maintained by LMKD:
adb shell cat /sys/module/lowmemorykiller/parameters/minfree 2>/dev/null || adb shell dumpsys activity processes | grep -A 6 "Low-memory killer minfree:"
The system displays the page thresholds corresponding to each OOM adjustment level. When free RAM falls below these thresholds, LMKD sends SIGKILL signals starting with the highest numbered cached processes.
Monitoring Real-Time Process Death Events in Logcat
To confirm whether a process was intentionally terminated by the Low Memory Killer due to OOM adjustment scoring rather than an unhandled Java/Kotlin crash, inspect logcat with the lmkd filter:
adb shell logcat -d -s lmkd:I ActivityManager:I | grep -E "Kill|am_kill" | tail -n 25
LMKD logs explicitly document the killed process name, PID, free memory at the moment of eviction, and the offending OOM adjustment level, providing definitive diagnostic proof for memory optimization audits.