Android 17 and higher supports memory management daemon
(mmd), a system daemon, that handles daemon configuration, tunables, and ongoing swap or ZRAM
maintenance tasks.
Background
Before the introduction of mmd, Android's ZRAM configurations were
fragmented and offered limited customization. mmd addresses this by
centralizing ZRAM management, enabling more sophisticated configuration logic
and simplifying the addition of new features and architectural improvements.
mmd also establishes a clear separation of concerns between the Java-based
system_server process and kernel-level swap or memory management.
Architecture and ZRAM management
On boot completion (that is, when sys.boot_completed=1), mmd_setup attempts
to configure ZRAM with specified parameters. After ZRAM setup is complete the
system enables the mmd service which handles ongoing maintenance tasks.
With the mmd project, maintenance operations are initiated from
system_server by sending Binder requests to mmd using the IMmd interface.
mmd handles the maintenance tasks of performing ZRAM writeback,
recompression, and per-process writeback based on its own internal policy
engine. Both the scheduling from ActivityManagerService and the ZRAM
maintenance policies can be configured using system properties.
System server integration (system_server)
The Java-based system_server process determines when mmd is invoked. The
process separates global maintenance sweeps from targeted per-app memory
optimizations.
Normal post-processing maintenance
Global ZRAM maintenance is driven by ActivityManagerService using
com.android.server.memory.ZramMaintenance.

Figure 1. ZRAM maintenance scheduling flow.
- Scheduling engine:
ZramMaintenanceregisters a periodic background job with Android'sJobScheduler. - Job constraints: To prevent foreground UI stuttering or CPU contention,
the job is explicitly configured with
setRequiresDeviceIdle(true)andsetRequiresBatteryNotLow(true). - Binder triggering: When the scheduler fires
onStartJob(),system_serverinvokesmmd.doZramMaintenanceAsync(). This is a one-way asynchronous Binder call;system_serverdoesn't block waiting for maintenance sweeps to finish.mmdqueues this onto a background worker thread to perform recompression and writeback sequentially.
Per-process writeback
Targeted per-process memory eviction is managed by ActivityManagerService using
com.android.server.am.CachedAppOptimizer.

Figure 2. mmd per-process writeback flow.
When a process transitions to a background cached state, ActivityManager performs memory compaction. If a low memory kill of the process would be visible to the user, that is, the process hosts an Activity, and if ZRAM per-process writeback would bring the process's memory footprint to near zero, then the system follows these steps:
- Following compaction,
CachedAppOptimizerposts a delayed message (ZRAM_WRITEBACK_MSG) to its internal compaction handler (delayed bymZramWritebackWaitSeconds). - When the delay expires, ActivityManager opens a secure process file
descriptor
pidfd. - The system server calls
mmd.asyncWritebackProcessZramMemory(pfd, callback). mmdexecutes the per-process writeback ioctl and reports back usingIMmdProcessWritebackCallback. If successful, ActivityManager flags the process record (setIsZramWrittenBack(app, true)) to boost the process'soom_score_adjand logs metrics toFrameworkStatsLog.ZRAM_WRITEBACK_EVENT.
Per-process prefetch
When a user re-launches a previously cached app (unfrozen due to
UNFREEZE_REASON_ACTIVITY), ActivityManager minimizes app startup
latency caused by major page faults from backing storage:
CachedAppOptimizerintercepts the unfreeze event and invokesprefetchZram(app).- The system server dispatches the app's
pidfdacross Binder usingmmd.asyncPrefetchProcessZramMemory(pfd).mmdissues theZRAM_ANDROID_IOC_PROCESS_PREFETCHioctl, instructing the kernel to asynchronously prefetch swapped pages back into RAM while the app's main UI thread is initializing.
Overview of maintenance and post-processing tasks
This section describes the background maintenance operations and
post-processing tasks that mmd runs to optimize swap space and system memory.
Maintenance in mmd
In mmd, maintenance refers to scheduled, background maintenance sweeps
that optimize swap space and physical memory utilization without impacting
active user foreground performance. Instead of performing continuous,
synchronous sweeps (which would cause severe CPU wakeups and UI jank),
maintenance is driven asynchronously:
system_serverperiodically firesdoZramMaintenanceAsync()across Binder.mmdplaces the request on a background work queueLowPrioWorkItem::ZramMaintenance.There is a single worker thread in
mmdthat manages both a high-priority queue and a low-priority queue. High-priority work items (such as per-process prefetch) are processed first and can preempt low-priority work items. Maintenance and per-process writeback operate as low-priority work items. When popped, the worker thread executes two primary maintenance operations sequentially:ZRAM recompression: Sweeps through existing swap pages and recompresses idle pages using a higher-ratio secondary compression algorithm, for example,
zstd.ZRAM writeback: Scans idle pages and evicts them entirely from RAM to backing flash storage a loop device from a file on
/data.
Post-processing tasks in ZRAM
In the Linux kernel ZRAM module and mmd architecture, post-processing
tasks are the asynchronous transformations applied to memory pages after
they have already been swapped out by the kernel's standard reclaim paths
(kswapd or compaction).
When a page is initially swapped out, the system prioritizes speed: it uses a
fast primary compression algorithm (like lz4) and stores the compressed page
in RAM. However, over time, many swapped pages become cold or idle,
for example, background cached apps that aren't resumed for hours. Leaving cold
pages in fast, lightly-compressed ZRAM is inefficient.
Post-processing pipeline
mmd implements a multi-stage post-processing lifecycle to optimize these
pages:

Figure 3. mmd page lifecycle.
Stage 1: Initial swap-out (fast compression): Memory is first reclaimed through kswapd or app compaction. Typically this first reclaim is performed using a fast compression algorithm such as
lz4is used and the contents are stored in RAM.Stage 2: Idle marking (aging & tracking):
mmdidle tracking accesses kernel memory tracking (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) or uses its software idle marker to track how long pages have remained untouched.Stage 3: Post-processing 1 - recompression (in-memory reclamation): Pages that reach recompression idle age (
min_idle_secondstomax_idle_seconds) undergo recompression.mmdwrites to/sys/block/zram0/recompressto instruct the kernel to decompress thelz4page and recompress it usingzstd. This reclaims physical RAM without incurring flash write wear.Stage 4: Post-processing 2 - writeback (eviction to flash storage): If memory pressure continues and pages reach writeback idle age (typically 20 hours or more),
mmdtriggers writeback.mmdwrites to/sys/block/zram0/idleand/sys/block/zram0/writebackto evict the compressed page entirely from RAM to backing flash storage.
ZRAM setup configuration
mmd loads and processes the following ZRAM setup properties:
| Property | Use | Default |
|---|---|---|
mmd.zram.enabled |
Whether mmd ZRAM setup is enabled. |
false |
mmd.zram.num_devices |
The number of ZRAM devices to configure. For a number N,
devices zram0 through zram<N-1> must be
present before the system sets sys.boot_completed=1.
Properties on the per-ZRAM device list can be configured on a per-device
basis.
|
1 |
mmd.zram.device_priority |
Priority values to pass when calling swapon. |
Not set |
mmd.zram.comp_algorithm |
ZRAM compression algorithm. Kernel default compression algorithm is used if not specified. | Not set |
mmd.zram.size |
ZRAM device size in bytes, or a percentage of device RAM size for example,
75%.
|
50% |
mmd.zram.writeback.enabled |
Whether to enable ZRAM writeback. | false |
mmd.zram.writeback.device_size |
The size of the writeback device in bytes or percentage of the data partition. The actual device size can be adjusted based on available space on the data partition. | 1073741824 (1 GiB) |
mmd.zram.writeback.min_free_space_mib |
Minimum free space in MiB that needs to be available after the writeback device is set up. | 1536 (1.5 GiB) |
mmd.zram.writeback.use_nr_tags_prop |
When true, uses the value in
mmd.zram.writeback.nr_tags to configure the queue depth of
the loop device backing ZRAM writeback. This is a workaround for
situations where vendor SELinux policy cannot be configured to allow
mmd to directly read nr_tags of the block
device backing /data.
|
false |
mmd.zram.writeback.nr_tags |
See mmd.zram.writeback.use_nr_tags_prop. |
Not set |
mmd.zram.recompression.enabled |
Whether to enable the ZRAM recompression feature. | false |
mmd.zram.recompression.algorithm |
Secondary ZRAM recompression algorithm. | zstd |
Per-ZRAM device properties
When mmd.zram.num_devices is greater than one, specific properties can
optionally be configured on a per-ZRAM device basis by setting the property to
a comma-separated value containing exactly mmd.zram.num_devices elements.
These properties include:
mmd.zram.sizemmd.zram.comp_algorithmmmd.zram.device_prioritymmd.zram.recompression.enabledmmd.zram.recompression.huge_idle.enabledmmd.zram.recompression.idle.enabledmmd.zram.recompression.huge.enabledmmd.zram.recompression.threshold_bytesmmd.zram.recompression.algorithmmmd.zram.writeback.device_sizemmd.zram.writeback.huge_idle.enabledmmd.zram.writeback.idle.enabledmmd.zram.writeback.huge.enabled
Existing ZRAM setup deprecation
While swapon_all is still available in Android to set up ZRAM and disk-based
swap space, mmd is the preferred approach for ZRAM management for easier
configuration and advanced features like ZRAM recompression.
When mmd ZRAM setup is enabled by mmd.zram.enabled:
- ZRAM setup in
swapon_allimplementation becomes a no-op. - Existing ZRAM configurations such as
config_zramWritebackin the overlayconfig.xmlfile andro.zram.*writeback system properties are ignored.
ZRAM maintenance tunables
ZRAM maintenance should work out of the box, and you can fine tune it further using the system properties in this section.
ZRAM maintenance scheduling
These properties control how and when ZRAM maintenance tasks are scheduled by
system_server.
| Property | Use | Default |
|---|---|---|
mm.zram.maintenance.first_delay_seconds |
The delay before the first ZRAM maintenance is initiated. | 3600 (1 hour) |
mm.zram.maintenance.periodic_delay_seconds |
The delay between subsequent ZRAM maintenance scheduling. | 3600 (1 hour) |
mm.zram.maintenance.require_device_idle |
Whether to only initiate ZRAM maintenance when the device is idle. | true |
mm.zram.maintenance.require_battery_not_low |
Whether to require battery not low before initiating ZRAM maintenance. | true |
ZRAM writeback policy
The following parameters control when and what type of memory is written to the backing device:
| Property | Use | Default |
|---|---|---|
mmd.zram.writeback.backoff_seconds |
The backoff time since the last writeback operation. | 600 (10 minutes) |
mmd.zram.writeback.min_idle_seconds |
Combined with mmd.zram.writeback.max_idle_seconds to calculate
the idle age for a page to be eligible for writeback based on memory
utilization fraction. The calculated idle age is exponentially interpolated
between the two parameters to minimize work while not under memory pressure.
|
72000 (20 hours) |
mmd.zram.writeback.max_idle_seconds |
Maximum seconds used for calculating idle page age dynamically based on memory utilization. | 90000 (25 hours) |
mmd.zram.writeback.huge.enabled |
Whether to enable HUGE page writeback. |
false |
mmd.zram.writeback.idle.enabled |
Whether to enable IDLE page writeback. |
true |
mmd.zram.writeback.huge_idle.enabled |
Whether to enable HUGE_IDLE page writeback. |
true |
mmd.zram.writeback.min_bytes |
Minimum bytes to write back in one round of idle writeback. | 5242880 (5 MiB) |
mmd.zram.writeback.max_bytes |
Maximum bytes to write back in one round of idle writeback. | 314572800 (300 MiB) |
mmd.zram.writeback.max_bytes_per_day |
Maximum bytes to write back in a 24-hour period. | 25769803776 (24 GiB) |
mmd.zram.writeback.limit.enabled |
Whether to enable daily writeback budget limit accounting. | true |
ZRAM recompression policy
The following parameters control when and what type of memory is recompressed:
| Property | Use | Default |
|---|---|---|
mmd.zram.recompression.backoff_seconds |
The backoff time since the last recompression. | 1800 (30 minutes) |
mmd.zram.recompression.min_idle_seconds |
Combined with mmd.zram.recompression.max_idle_seconds to
calculate the idle age for a page to be eligible for recompression based on
memory utilization fraction. The calculated idle age is exponentially
interpolated between the two parameters to minimize work while not under
memory pressure.
|
7200 (2 hours) |
mmd.zram.recompression.max_idle_seconds |
Maximum seconds used for calculating idle page age dynamically. | 14400 (4 hours) |
mmd.zram.recompression.threshold_bytes |
Minimum size in bytes of ZRAM pages considered for recompression. | 1024 (1 KiB) |
mmd.zram.recompression.huge.enabled |
Whether to enable HUGE page recompression. |
true |
mmd.zram.recompression.idle.enabled |
Whether to enable IDLE page recompression. |
true |
mmd.zram.recompression.huge_idle.enabled |
Whether to enable HUGE_IDLE page recompression. |
true |
ZRAM idle page tracking
mmd ZRAM maintenance marks ZRAM pages as idle based on how long it has been
since they were last accessed. This feature requires the
CONFIG_ZRAM_TRACK_ENTRY_ACTIME or CONFIG_ZRAM_MEMORY_TRACKING kernel
configurations to be enabled. CONFIG_ZRAM_TRACK_ENTRY_ACTIME is enabled by
default on GKI kernels 6.18 and higher. On earlier kernels,
it has memory overhead and isn't enabled by default.
If the kernel configuration isn't enabled, mmd ZRAM maintenance falls back to
a software substitute logic to track idle ZRAM pages:
Mark all ZRAM pages as idle when
mmdstarts.Skip next ZRAM maintenances until required backoff period has passed.
ZRAM writeback or recompress idle pages. If there are remaining idle pages due to writeback limits,
mmdcontinues to write back pages on the next maintenance without marking new pages as idle (skipping step 4).If all idle pages are written back, mark all ZRAM pages as idle again and move back to step 2. If ZRAM writeback is disabled,
mmdmarks all ZRAM pages as idle when ZRAM recompression happens after the recompression idle duration.
Troubleshoot and validation guidance
Use the following validation steps and troubleshooting procedures to verify and
diagnose mmd and ZRAM operations.
Validate ZRAM setup
To verify that mmd successfully configured ZRAM during boot:
Check the active compression algorithm and disk size:
cat /sys/block/zram0/comp_algorithm cat /sys/block/zram0/disksizeVerify
mmdsystem properties and running service state:getprop | grep mmd.zram dumpsys -l | grep mmd
Validate ZRAM maintenance and writeback
Verify that ZRAM writeback and recompression maintenance tasks are functioning:
Check the backing block device status:
cat /sys/block/zram0/bd_statCheck recompression efficiency by monitoring
/sys/block/zram0/mm_stat. Changes in compressed data sizes should appear after maintenance cycles.
Validate per-process writeback
The following can be used to validate that per-process writeback is functioning:
- Check
adb logcat -s mmdfor successful writeback logs or failure diagnostics.
Common issues and diagnostics
The following are common error situations the user might run into:
WritebackDailyLimitExceeded: This error indicates that themmd.zram.writeback.max_bytes_per_dayquota has been reached. When this occurs,mmdpauses idle writeback until the 24-hour rolling window advances.Process prefetch or writeback failed: This error can be observed in logcat when an ioctl fails. Common causes include:EBADForESRCH: The target process ended beforemmdcould dispatch thepidfdto the kernel.ENOSPC: The backing storage partition is full, or the loop device queue is exhausted.
- ZRAM not set up: If
mmdfails to configure ZRAM on boot, it might be because legacyswapon_allor vendor init scripts locked/dev/block/zram0beforemmdcould execute.