Mobile devices operate under memory constraints that desktop and server systems simply don’t face in the same way: limited RAM, no (or very limited) traditional swap space, flash storage with finite write endurance, and an overriding priority on battery life and UI responsiveness. Yet these devices still need a strategy for deciding what to keep in memory and what to evict when RAM runs low. That strategy is the page replacement algorithm, and on mobile systems it plays a role that’s both familiar from classic OS theory and meaningfully adapted for mobile’s unique constraints.
What Is a Page Replacement Algorithm?
In any virtual memory system, physical RAM is a finite resource shared across all running processes. When the system needs to bring a new page into memory (because of a page fault) but no free physical frames are available, it must choose an existing page to evict — write it out (if necessary) and reclaim its frame for the new page. The page replacement algorithm is the policy that decides which page gets evicted.
Classic Page Replacement Algorithms
- FIFO (First-In-First-Out): Evicts the oldest-loaded page, regardless of usage — simple but can perform poorly (subject to Belady’s Anomaly, where adding more memory can paradoxically increase page faults).
- LRU (Least Recently Used): Evicts the page that hasn’t been accessed for the longest time, based on the principle of temporal locality (recently used pages are likely to be used again soon). Performs well in practice but requires tracking access recency, which has real overhead.
- Clock / Second-Chance Algorithm: An efficient approximation of LRU using a reference bit and a circular (“clock hand”) scan, avoiding the overhead of maintaining exact access-time ordering while still capturing much of LRU’s benefit.
- LFU (Least Frequently Used): Evicts the page accessed least often — theoretically appealing but can unfairly penalize newly loaded pages that haven’t had time to accumulate access counts.
- Working Set Model: Tracks the set of pages a process has actively used within a recent time window, aiming to keep a process’s “working set” resident to avoid thrashing.
Why Page Replacement Matters More — and Differently — on Mobile
1. RAM Is a Precious, Tightly Constrained Resource
Mobile devices, despite steady RAM increases over the years (from a few hundred MB in early smartphones to 8–16 GB in current flagships), still operate with far less memory relative to their workloads than desktops or servers, especially given the number of apps users expect to keep “open” simultaneously. Efficient page replacement directly determines how many apps can stay resident in memory without noticeable slowdowns or forced restarts.
2. Traditional Disk-Based Swap Is Largely Avoided
Unlike desktop/server systems, which typically use a dedicated swap partition or swap file on disk to extend effective memory, most mobile operating systems historically avoided traditional swap-to-disk almost entirely, for two key reasons:
- Flash storage write endurance: NAND flash storage (used in virtually all mobile devices) has a limited number of write/erase cycles before cells wear out. Constant swapping — which involves frequent writes — would accelerate storage degradation.
- Performance: Flash storage, while much faster than old spinning disks, is still dramatically slower than RAM, and swapping under memory pressure could introduce noticeable UI stutter, directly harming the perceived responsiveness mobile users expect.
3. Alternative Strategies Take the Place of Traditional Swapping
Because disk-based swap was historically avoided, mobile OSes developed alternative memory reclamation strategies where page replacement algorithms still play a central conceptual role, just applied differently:
- Process termination instead of swapping (classic Android/iOS model): When memory runs low, rather than swapping a background app’s memory to disk, the OS simply kills the least recently used background process entirely, relying on the app’s ability to save state and relaunch later. This is, functionally, an LRU-based “replacement” policy — but applied at the process level rather than the individual page level. Android’s Low Memory Killer (LMK), and its more refined successor system, LMKD (Low Memory Killer Daemon), maintain an “oom_adj”/priority ranking of processes (foreground, visible, service, cached/background) and kill the least important, least recently used background processes first when memory pressure rises.
- Compressed memory (zRAM/zswap): Modern Android devices widely use zRAM, a compressed RAM-based swap area — instead of writing evicted pages to flash storage, the kernel compresses them and keeps them in a reserved portion of RAM itself. This still relies on a page replacement algorithm (Linux’s kernel default is an LRU-based approximation) to decide which pages to compress and move into the zRAM pool, but avoids flash wear and the latency penalty of actual disk I/O.
- iOS’s compressed memory and “jetsam”: iOS uses a broadly similar model — background apps are frozen (compressed in memory, not paged to flash) and, under sufficient memory pressure, terminated by the “jetsam” mechanism, again fundamentally an LRU-influenced eviction policy applied at the process/app level, prioritizing keeping the foreground app and recently used background apps alive longest.
How the Underlying Kernel Page Replacement Still Operates
Even with these mobile-specific adaptations layered on top, the underlying Linux kernel (which powers Android, and shares conceptual DNA with the XNU kernel underlying iOS/macOS) still runs its standard page reclaim machinery for anonymous and file-backed pages:
- The Linux kernel maintains active and inactive LRU lists for both anonymous (heap/stack) and file-backed (mmap’d, page-cache) pages.
- Under memory pressure, the kernel’s
kswapd(and direct reclaim path) scans these lists, moving pages between active/inactive states based on recent access patterns (tracked via page table “accessed” bits), and reclaims from the inactive list first. - On Android specifically, file-backed (clean) pages backed by files that can simply be re-read from storage (like
.dex/.oatfiles, memory-mapped APK resources) are cheap to evict — they’re just dropped and re-faulted in later if needed, no write-back required. - Anonymous pages (heap/stack memory not backed by a file) are the ones that, on a traditional Linux desktop/server, would go to swap; on mobile, they’re the ones eligible for zRAM compression or ultimately trigger app termination via LMKD/jetsam if memory pressure remains unresolved.
A Simplified View of Mobile Memory Reclamation Layers
Memory pressure rises
|
v
1. Reclaim clean file-backed pages (cheap: drop and re-fault later)
|
v
2. Compress and move anonymous pages to zRAM (Android) / compressed memory (iOS)
|
v
3. If pressure persists: kill least-recently-used background process
(Android LMKD / iOS jetsam) — an LRU-based decision at process granularity
Real-World Examples
Android
Android’s memory management explicitly ranks running processes by an “OOM adjustment” score reflecting importance (foreground activity, visible activity, background service, cached process), and within each tier, recency of use (an LRU signal) determines which processes are killed first when LMKD needs to free memory — a direct, practical application of least-recently-used thinking, just at a coarser granularity (whole processes) than classic textbook page replacement (individual pages).
iOS
iOS’s jetsam subsystem similarly evaluates memory pressure and terminates background apps based on a combination of memory footprint and recency/priority, aiming to preserve the currently active app’s responsiveness above all else — again an LRU-flavored policy, tightly integrated with Apple’s app lifecycle and state-preservation APIs (allowing terminated apps to “resume” with saved state, masking the termination from the user’s perspective as much as possible).
Linux-Based Embedded/Mobile Variants
Custom embedded Linux devices (and platforms like postmarketOS or other mobile Linux distributions) that do support traditional swap (sometimes to a swap file on flash, accepted deliberately for specific use cases) still rely on the standard Linux kernel’s LRU-based kswapd reclaim logic, sometimes tuned via the vm.swappiness parameter to bias the kernel more toward or against swapping relative to reclaiming file-backed pages.
Comparison: Desktop/Server vs. Mobile Page Replacement Approach
| Aspect | Desktop/Server | Mobile (Android/iOS typical) |
|---|---|---|
| Primary swap medium | Disk/SSD swap partition or file | zRAM (compressed RAM) primarily; disk swap avoided |
| Eviction granularity | Individual pages | Individual pages (zRAM) + whole processes (LMKD/jetsam) |
| Underlying algorithm | LRU/Clock approximation | LRU/Clock approximation, same kernel machinery on Android |
| Flash wear consideration | Generally not a primary concern (or SSD wear leveling handles it) | Major design consideration; avoided via zRAM |
| User-visible consequence of eviction | Slower access to swapped data | App restart/state loss if killed and not properly restored |
Troubleshooting Common Mobile Memory Reclamation Issues
- App loses its state/data when reopened after being backgrounded: Usually means the app was terminated by LMKD (Android) or jetsam (iOS) and failed to properly save/restore its state — a common app-development bug, not necessarily an OS defect.
- Device feels sluggish despite “free” RAM reported: Modern mobile OSes deliberately keep RAM full of cached background app data (rather than truly “free,” unused RAM) as a performance optimization; low reported “free” memory is often normal and not indicative of a problem.
- Excessive background app kills on a device with seemingly adequate RAM: Check for a misbehaving foreground app consuming excessive memory, forcing aggressive LMKD/jetsam action against other background processes; profile memory usage with platform tools (Android Studio Profiler, Xcode Instruments).
- On rooted/custom ROM Android devices, tuning zRAM/swappiness incorrectly: Overly aggressive swappiness settings can increase compression/decompression CPU overhead and reduce battery life without meaningfully improving usable memory; test changes carefully rather than assuming more swap tuning is always better.
Best Practices
- Mobile app developers should implement proper state-saving (Android’s
onSaveInstanceState/ViewModel persistence, iOS’s state restoration APIs) so process termination by LMKD/jetsam is seamless to the user. - Avoid holding unnecessarily large in-memory caches in background app states; release non-essential memory proactively when your app receives low-memory callbacks (
onTrimMemoryon Android,didReceiveMemoryWarninghistorically on iOS). - When customizing embedded Linux mobile/IoT devices, tune
vm.swappinessand zRAM configuration deliberately based on actual measured memory pressure patterns, not default assumptions carried over from desktop Linux tuning. - Rely on platform-provided memory profiling tools rather than guessing at memory pressure causes from user-reported symptoms alone.
- Design apps to degrade gracefully under memory pressure (e.g., releasing cached images) rather than assuming unlimited background memory availability.
Summary
Page replacement algorithms — rooted in classic OS theory (FIFO, LRU, Clock, working set) — remain conceptually central to mobile memory management, but mobile operating systems apply them in adapted forms suited to flash storage constraints and battery/performance priorities. Rather than traditional disk-based swapping, Android and iOS lean heavily on RAM-based compression (zRAM, compressed memory) and LRU-influenced process termination (LMKD, jetsam) to reclaim memory under pressure, while the underlying Linux kernel (for Android) still runs standard LRU-based page reclaim machinery for individual pages beneath these higher-level policies. Understanding this layered approach is essential for both mobile app developers optimizing for memory pressure and engineers customizing embedded mobile Linux systems.
FAQs
Q: Do Android and iOS use traditional disk-based swap? Generally no, historically — they’ve favored RAM-based compression (zRAM on Android, compressed memory on iOS) and process termination over writing to flash storage, primarily to avoid flash wear and latency penalties, though some devices/configurations have experimented with limited disk-based swap.
Q: What replaces classic page-level swapping on mobile? A combination of zRAM/compressed memory (still page-level, but RAM-to-RAM rather than RAM-to-disk) and whole-process termination via LMKD (Android) or jetsam (iOS) when memory pressure persists beyond what compression alone can resolve.
Q: Is process termination the same thing as page replacement? Not identical, but conceptually related — it applies similar recency-of-use (“least recently used”) logic at the granularity of whole processes rather than individual memory pages.
Q: Why do mobile devices avoid traditional swap-to-flash? Primarily due to NAND flash’s limited write endurance (frequent swap writes would accelerate wear) and because flash access, while fast, still introduces latency that can degrade responsiveness compared to RAM-based approaches.
Q: What is zRAM? A Linux kernel feature that creates a compressed block device in RAM itself, used as a swap target — pages are compressed and stored in this RAM-based area instead of being written to disk/flash, trading some CPU overhead for avoiding flash wear and slow storage I/O.
References
- Android Open Source Project: “Low Memory Killer Daemon (LMKD)” documentation — source.android.com
- Apple Developer Documentation: “Responding to Low-Memory Warnings” and app state restoration
- Linux Kernel Documentation: zRAM (
Documentation/admin-guide/blockdev/zram.rst) - “Operating System Concepts” by Silberschatz, Galvin, and Gagne — Page Replacement Algorithms chapter