Memory management daemon

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.

zram-maintenance

Figure 1. ZRAM maintenance scheduling flow.

  • Scheduling engine: ZramMaintenance registers a periodic background job with Android's JobScheduler.
  • Job constraints: To prevent foreground UI stuttering or CPU contention, the job is explicitly configured with setRequiresDeviceIdle(true) and setRequiresBatteryNotLow(true).
  • Binder triggering: When the scheduler fires onStartJob(), system_server invokes mmd.doZramMaintenanceAsync(). This is a one-way asynchronous Binder call; system_server doesn't block waiting for maintenance sweeps to finish. mmd queues 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.

mmd-writeback

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:

  1. Following compaction, CachedAppOptimizer posts a delayed message (ZRAM_WRITEBACK_MSG) to its internal compaction handler (delayed by mZramWritebackWaitSeconds).
  2. When the delay expires, ActivityManager opens a secure process file descriptor pidfd.
  3. The system server calls mmd.asyncWritebackProcessZramMemory(pfd, callback).
  4. mmd executes the per-process writeback ioctl and reports back using IMmdProcessWritebackCallback. If successful, ActivityManager flags the process record (setIsZramWrittenBack(app, true)) to boost the process's oom_score_adj and logs metrics to FrameworkStatsLog.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:

  1. CachedAppOptimizer intercepts the unfreeze event and invokes prefetchZram(app).
  2. The system server dispatches the app's pidfd across Binder using mmd.asyncPrefetchProcessZramMemory(pfd). mmd issues the ZRAM_ANDROID_IOC_PROCESS_PREFETCH ioctl, 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:

  1. system_server periodically fires doZramMaintenanceAsync() across Binder.

  2. mmd places the request on a background work queue LowPrioWorkItem::ZramMaintenance.

  3. There is a single worker thread in mmd that 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:

mmd-page-lifecycle

Figure 3. mmd page lifecycle.

  1. 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 lz4 is used and the contents are stored in RAM.

  2. Stage 2: Idle marking (aging & tracking): mmd idle tracking accesses kernel memory tracking (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) or uses its software idle marker to track how long pages have remained untouched.

  3. Stage 3: Post-processing 1 - recompression (in-memory reclamation): Pages that reach recompression idle age (min_idle_seconds to max_idle_seconds) undergo recompression. mmd writes to /sys/block/zram0/recompress to instruct the kernel to decompress the lz4 page and recompress it using zstd. This reclaims physical RAM without incurring flash write wear.

  4. 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), mmd triggers writeback. mmd writes to /sys/block/zram0/idle and /sys/block/zram0/writeback to 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.size
  • mmd.zram.comp_algorithm
  • mmd.zram.device_priority
  • mmd.zram.recompression.enabled
  • mmd.zram.recompression.huge_idle.enabled
  • mmd.zram.recompression.idle.enabled
  • mmd.zram.recompression.huge.enabled
  • mmd.zram.recompression.threshold_bytes
  • mmd.zram.recompression.algorithm
  • mmd.zram.writeback.device_size
  • mmd.zram.writeback.huge_idle.enabled
  • mmd.zram.writeback.idle.enabled
  • mmd.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_all implementation becomes a no-op.
  • Existing ZRAM configurations such as config_zramWriteback in the overlay config.xml file and ro.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:

  1. Mark all ZRAM pages as idle when mmd starts.

  2. Skip next ZRAM maintenances until required backoff period has passed.

  3. ZRAM writeback or recompress idle pages. If there are remaining idle pages due to writeback limits, mmd continues to write back pages on the next maintenance without marking new pages as idle (skipping step 4).

  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, mmd marks 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:

  1. Check the active compression algorithm and disk size:

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. Verify mmd system 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:

  1. Check the backing block device status:

    cat /sys/block/zram0/bd_stat
    
  2. Check 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 mmd for 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 the mmd.zram.writeback.max_bytes_per_day quota has been reached. When this occurs, mmd pauses 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:
    • EBADF or ESRCH: The target process ended before mmd could dispatch the pidfd to the kernel.
    • ENOSPC: The backing storage partition is full, or the loop device queue is exhausted.
  • ZRAM not set up: If mmd fails to configure ZRAM on boot, it might be because legacy swapon_all or vendor init scripts locked /dev/block/zram0 before mmd could execute.