Android 17 以降では、デーモン構成、チューニング可能値、進行中のスワップまたは ZRAM メンテナンス タスクを処理するシステム デーモンであるメモリ管理デーモン(mmd)がサポートされています。
背景
mmd が導入される前は、Android の ZRAM 構成は断片化されており、カスタマイズの範囲も限られていました。mmd は、ZRAM 管理を一元化し、より高度な構成ロジックを可能にし、新機能の追加とアーキテクチャの改善を簡素化することで、この問題に対処します。mmd は、Java ベースの system_server プロセスとカーネルレベルのスワップまたはメモリ管理の間の関心事の明確な分離も確立します。
アーキテクチャと ZRAM 管理
起動が完了すると(つまり、sys.boot_completed=1 の場合)、mmd_setup は指定されたパラメータで ZRAM を構成しようとします。ZRAM のセットアップが完了すると、システムは継続的なメンテナンス タスクを処理する mmd サービスを有効にします。
mmd プロジェクトでは、IMmd インターフェースを使用して mmd に Binder リクエストを送信することで、system_server からメンテナンス オペレーションが開始されます。mmd は、独自の内部ポリシー エンジンに基づいて、ZRAM ライトバック、再圧縮、プロセスごとのライトバックを実行するメンテナンス タスクを処理します。ActivityManagerService からのスケジューリングと ZRAM メンテナンス ポリシーは、どちらもシステム プロパティを使用して設定できます。
システム サーバーの統合(system_server)
Java ベースの system_server プロセスは、mmd が呼び出されるタイミングを決定します。このプロセスでは、グローバル メンテナンス スイープとアプリごとのメモリ最適化が分離されます。
通常の後処理メンテナンス
グローバル ZRAM メンテナンスは、com.android.server.memory.ZramMaintenance を使用して ActivityManagerService によって駆動されます。

図 1. ZRAM メンテナンスのスケジュール設定フロー。
- スケジューリング エンジン:
ZramMaintenanceは、Android のJobSchedulerに定期的なバックグラウンド ジョブを登録します。 - ジョブの制約: フォアグラウンド UI のスタッタリングや CPU の競合を防ぐため、ジョブは
setRequiresDeviceIdle(true)とsetRequiresBatteryNotLow(true)で明示的に設定されます。 - バインダのトリガー: スケジューラが
onStartJob()を起動すると、system_serverがmmd.doZramMaintenanceAsync()を呼び出します。これは一方向の非同期 Binder 呼び出しです。system_serverは、メンテナンス スイープの完了を待機してブロックしません。mmdは、再圧縮と書き戻しを順番に実行するために、これをバックグラウンド ワーカー スレッドにキューに入れます。
プロセス単位の書き戻し
プロセスごとのメモリ削除のターゲットは、ActivityManagerService が com.android.server.am.CachedAppOptimizer を使用して管理します。

図 2. mmd プロセスごとの書き込みフロー。
プロセスがバックグラウンド キャッシュ状態に移行すると、ActivityManager はメモリ圧縮を実行します。プロセスの Low Memory Kill がユーザーに表示される場合(プロセスが Activity をホストしている場合)、かつ ZRAM のプロセスごとの書き戻しによってプロセスのメモリ使用量がほぼゼロになる場合、システムは次の手順を行います。
- コンパクションの後、
CachedAppOptimizerは遅延メッセージ(ZRAM_WRITEBACK_MSG)を内部コンパクション ハンドラに投稿します(mZramWritebackWaitSecondsの遅延)。 - 遅延が終了すると、ActivityManager はセキュア プロセス ファイル記述子
pidfdを開きます。 - システム サーバーが
mmd.asyncWritebackProcessZramMemory(pfd, callback)を呼び出します。 mmdはプロセスごとのライトバック ioctl を実行し、IMmdProcessWritebackCallbackを使用してレポートを返します。成功すると、ActivityManager はプロセス レコード(setIsZramWrittenBack(app, true))にフラグを設定してプロセスのoom_score_adjをブーストし、FrameworkStatsLog.ZRAM_WRITEBACK_EVENTに指標を記録します。
プロセスごとのプリフェッチ
ユーザーが以前にキャッシュに保存されたアプリを再起動すると(UNFREEZE_REASON_ACTIVITY によりフリーズが解除される)、ActivityManager は、バッキング ストレージからのメジャー ページフォルトによって発生するアプリの起動レイテンシを最小限に抑えます。
CachedAppOptimizerはフリーズ解除イベントをインターセプトし、prefetchZram(app)を呼び出します。- システム サーバーは、
mmd.asyncPrefetchProcessZramMemory(pfd)を使用して Binder 経由でアプリのpidfdをディスパッチします。mmdはZRAM_ANDROID_IOC_PROCESS_PREFETCHioctl を発行し、アプリのメイン UI スレッドの初期化中にスワップされたページを非同期で RAM にプリフェッチするようカーネルに指示します。
メンテナンス タスクと後処理タスクの概要
このセクションでは、mmd がスワップ領域とシステム メモリを最適化するために実行するバックグラウンド メンテナンス オペレーションと後処理タスクについて説明します。
mmd でのメンテナンス
mmd では、メンテナンスとは、アクティブ ユーザーのフォアグラウンド パフォーマンスに影響を与えることなく、スワップ領域と物理メモリの使用率を最適化する、スケジュールされたバックグラウンド メンテナンス スイープを指します。継続的な同期スイープ(CPU の過剰なウェイクアップと UI ジャンクの原因となる)を実行する代わりに、メンテナンスは非同期で実行されます。
system_serverは Binder 全体でdoZramMaintenanceAsync()を定期的に起動します。mmdは、リクエストをバックグラウンド作業キューLowPrioWorkItem::ZramMaintenanceに配置します。mmdには、優先度の高いキューと優先度の低いキューの両方を管理する単一のワーカー スレッドがあります。優先度の高いワークアイテム(プロセスごとのプリフェッチなど)が最初に処理され、優先度の低いワークアイテムをプリエンプトできます。メンテナンスとプロセスごとの書き込みは、優先度の低い作業項目として動作します。ポップされると、ワーカー スレッドは 2 つの主なメンテナンス オペレーションを順番に実行します。ZRAM 再圧縮: 既存のスワップページをスキャンし、より高い比率のセカンダリ圧縮アルゴリズム(
zstdなど)を使用してアイドル状態のページを再圧縮します。ZRAM ライトバック: アイドル状態のページをスキャンし、それらを RAM から
/dataのファイルにあるループ デバイスのバックアップ フラッシュ ストレージに完全に退避させます。
ZRAM での後処理タスク
Linux カーネルの ZRAM モジュールと mmd アーキテクチャでは、後処理タスクは、カーネルの標準の再利用パス(kswapd またはコンパクション)によってすでにスワップアウトされたメモリページに適用される非同期変換です。
ページが最初にスワップアウトされるとき、システムは速度を優先します。高速なプライマリ圧縮アルゴリズム(lz4 など)を使用して、圧縮されたページを RAM に保存します。ただし、時間が経つと、スワップされたページの多くはコールドまたはアイドル状態になります(たとえば、数時間再開されないバックグラウンド キャッシュ アプリなど)。コールドページを高速で圧縮率の低い ZRAM に残しておくのは非効率です。
後処理パイプライン
mmd は、これらのページを最適化するために、マルチステージのポスト プロセッシング ライフサイクルを実装します。

図 3. mmd ページのライフサイクル。
ステージ 1: 最初のスワップアウト(高速圧縮): メモリは、まず kswapd またはアプリの圧縮によって再利用されます。通常、この最初の再利用は
lz4などの高速圧縮アルゴリズムを使用して実行され、コンテンツは RAM に保存されます。ステージ 2: アイドル状態のマーキング(エージングとトラッキング):
mmdアイドル状態のトラッキングは、カーネル メモリ トラッキング(CONFIG_ZRAM_TRACK_ENTRY_ACTIME)にアクセスするか、ソフトウェア アイドル マーカーを使用して、ページがどのくらいの期間アクセスされていないかをトラッキングします。ステージ 3: 後処理 1 - 再圧縮(メモリ内再利用): 再圧縮のアイドル状態の期間(
min_idle_secondsからmax_idle_seconds)に達したページは再圧縮されます。mmdは/sys/block/zram0/recompressに書き込み、lz4ページを解凍してzstdを使用して再圧縮するようカーネルに指示します。これにより、フラッシュ書き込みの摩耗を発生させることなく、物理 RAM を再利用できます。ステージ 4: 後処理 2 - 書き戻し(フラッシュ ストレージへの削除): メモリ負荷が継続し、ページが書き戻しアイドル状態の期間(通常は 20 時間以上)に達すると、
mmdが書き戻しをトリガーします。mmdは/sys/block/zram0/idleと/sys/block/zram0/writebackに書き込み、圧縮されたページを RAM からバッキング フラッシュ ストレージに完全に削除します。
ZRAM のセットアップ構成
mmd は、次の ZRAM 設定プロパティを読み込んで処理します。
| プロパティ | 用途 | デフォルト |
|---|---|---|
mmd.zram.enabled |
mmd ZRAM 設定が有効かどうか。 |
false |
mmd.zram.num_devices |
構成する ZRAM デバイスの数。数値 N の場合、システムが sys.boot_completed=1 を設定する前に、デバイス zram0 から zram<N-1> が存在している必要があります。ZRAM デバイスごとのリストのプロパティは、デバイスごとに構成できます。 |
1 |
mmd.zram.device_priority |
swapon を呼び出すときに渡す優先度値。 |
未設定 |
mmd.zram.comp_algorithm |
ZRAM 圧縮アルゴリズム。指定しない場合、カーネルのデフォルトの圧縮アルゴリズムが使用されます。 | 未設定 |
mmd.zram.size |
ZRAM デバイスのサイズ(バイト単位)、またはデバイスの RAM サイズの割合(75% など)。 |
50% |
mmd.zram.writeback.enabled |
ZRAM ライトバックを有効にするかどうか。 | false |
mmd.zram.writeback.device_size |
ライトバック デバイスのサイズ(バイト単位またはデータ パーティションの割合)。実際のデバイスサイズは、データ パーティションで使用可能なスペースに基づいて調整できます。 | 1073741824(1 GiB) |
mmd.zram.writeback.min_free_space_mib |
書き込みデバイスの設定後に利用可能にする必要がある最小空き容量(MiB 単位)。 | 1536(1.5 GiB) |
mmd.zram.writeback.use_nr_tags_prop |
true の場合、mmd.zram.writeback.nr_tags の値を使用して、ZRAM 書き戻しをサポートするループデバイスのキューの深さを構成します。これは、ベンダーの SELinux ポリシーを構成して mmd が /data をバックアップするブロック デバイスの nr_tags を直接読み取ることができない状況に対する回避策です。 |
false |
mmd.zram.writeback.nr_tags |
mmd.zram.writeback.use_nr_tags_prop をご覧ください。 |
未設定 |
mmd.zram.recompression.enabled |
ZRAM 再圧縮機能を有効にするかどうか。 | false |
mmd.zram.recompression.algorithm |
セカンダリ ZRAM 再圧縮アルゴリズム。 | zstd |
ZRAM デバイスごとのプロパティ
mmd.zram.num_devices が 1 より大きい場合、プロパティを mmd.zram.num_devices 個の要素を正確に含むカンマ区切りの値に設定することで、特定のプロパティを ZRAM デバイスごとに任意で構成できます。次のようなプロパティがあります。
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
既存の ZRAM 設定の非推奨化
Android では swapon_all を使用して ZRAM とディスクベースのスワップ領域を設定できますが、ZRAM の管理には mmd を使用することをおすすめします。これにより、構成が容易になり、ZRAM の再圧縮などの高度な機能を利用できるようになります。
mmd.zram.enabled によって mmd ZRAM 設定が有効になっている場合:
swapon_all実装の ZRAM 設定が no-op になります。- オーバーレイ
config.xmlファイルのconfig_zramWritebackやro.zram.*書き戻しシステム プロパティなどの既存の ZRAM 構成は無視されます。
ZRAM メンテナンス チューナブル
ZRAM のメンテナンスはすぐに利用でき、このセクションのシステム プロパティを使用してさらに微調整できます。
ZRAM メンテナンスのスケジュール設定
これらのプロパティは、system_server が ZRAM メンテナンス タスクをスケジュールする方法とタイミングを制御します。
| プロパティ | 用途 | デフォルト |
|---|---|---|
mm.zram.maintenance.first_delay_seconds |
最初の ZRAM メンテナンスが開始されるまでの遅延。 | 3600(1 時間) |
mm.zram.maintenance.periodic_delay_seconds |
後続の ZRAM メンテナンス スケジューリング間の遅延。 | 3600(1 時間) |
mm.zram.maintenance.require_device_idle |
デバイスがアイドル状態のときにのみ ZRAM メンテナンスを開始するかどうか。 | true |
mm.zram.maintenance.require_battery_not_low |
ZRAM メンテナンスを開始する前にバッテリー残量が少ないことを要求するかどうか。 | true |
ZRAM 書き込みポリシー
次のパラメータは、バッキング デバイスに書き込まれるメモリのタイミングとタイプを制御します。
| プロパティ | 用途 | デフォルト |
|---|---|---|
mmd.zram.writeback.backoff_seconds |
前回のライトバック オペレーションからのバックオフ時間。 | 600(10 分) |
mmd.zram.writeback.min_idle_seconds |
mmd.zram.writeback.max_idle_seconds と組み合わせて、メモリ使用率に基づいてページがライトバックの対象となるまでのアイドル時間を計算します。計算されたアイドル状態の経過時間は、メモリ負荷がかかっていないときに作業を最小限に抑えるために、2 つのパラメータ間で指数関数的に補間されます。 |
72000(20 時間) |
mmd.zram.writeback.max_idle_seconds |
メモリ使用率に基づいてアイドル ページの経過時間を動的に計算するために使用される最大秒数。 | 90000(25 時間) |
mmd.zram.writeback.huge.enabled |
HUGE ページのライトバックを有効にするかどうか。 |
false |
mmd.zram.writeback.idle.enabled |
IDLE ページのライトバックを有効にするかどうか。 |
true |
mmd.zram.writeback.huge_idle.enabled |
HUGE_IDLE ページのライトバックを有効にするかどうか。 |
true |
mmd.zram.writeback.min_bytes |
アイドル書き戻しの 1 回のラウンドで書き戻す最小バイト数。 | 5242880(5 MiB) |
mmd.zram.writeback.max_bytes |
アイドル書き戻し 1 回で書き戻す最大バイト数。 | 314572800(300 MiB) |
mmd.zram.writeback.max_bytes_per_day |
24 時間以内に書き戻す最大バイト数。 | 25769803776(24 GiB) |
mmd.zram.writeback.limit.enabled |
1 日の書き戻し予算上限の会計処理を有効にするかどうか。 | true |
ZRAM 再圧縮ポリシー
次のパラメータは、メモリの再圧縮のタイミングとタイプを制御します。
| プロパティ | 用途 | デフォルト |
|---|---|---|
mmd.zram.recompression.backoff_seconds |
前回の再圧縮からのバックオフ時間。 | 1800(30 分) |
mmd.zram.recompression.min_idle_seconds |
mmd.zram.recompression.max_idle_seconds と組み合わせて、メモリ使用率に基づいて再圧縮の対象となるページのアイドル時間を計算します。計算されたアイドル状態の年齢は、メモリの負荷がかかっていないときに作業を最小限に抑えるために、2 つのパラメータ間で指数関数的に補間されます。 |
7200(2 時間) |
mmd.zram.recompression.max_idle_seconds |
アイドル状態のページの経過時間を動的に計算するために使用される最大秒数。 | 14400(4 時間) |
mmd.zram.recompression.threshold_bytes |
再圧縮の対象となる ZRAM ページの最小サイズ(バイト単位)。 | 1024(1 KiB) |
mmd.zram.recompression.huge.enabled |
HUGE ページの再圧縮を有効にするかどうか。 |
true |
mmd.zram.recompression.idle.enabled |
IDLE ページの再圧縮を有効にするかどうか。 |
true |
mmd.zram.recompression.huge_idle.enabled |
HUGE_IDLE ページの再圧縮を有効にするかどうか。 |
true |
ZRAM アイドル状態のページ トラッキング
mmd ZRAM メンテナンスは、最後にアクセスされてからの経過時間に基づいて、ZRAM ページをアイドル状態としてマークします。この機能を使用するには、CONFIG_ZRAM_TRACK_ENTRY_ACTIME または CONFIG_ZRAM_MEMORY_TRACKING カーネル構成を有効にする必要があります。GKI カーネル 6.18 以降では、CONFIG_ZRAM_TRACK_ENTRY_ACTIME がデフォルトで有効になっています。以前のカーネルでは、メモリのオーバーヘッドがあり、デフォルトでは有効になっていません。
カーネル構成が有効になっていない場合、mmd ZRAM メンテナンスは、アイドル状態の ZRAM ページを追跡するソフトウェア代替ロジックにフォールバックします。
mmdの起動時にすべての ZRAM ページをアイドル状態としてマークします。必要なバックオフ期間が経過するまで、次の ZRAM メンテナンスをスキップします。
ZRAM のライトバックまたはアイドル状態のページの再圧縮。ライトバックの制限によりアイドル状態のページが残っている場合、
mmdは次のメンテナンスで新しいページをアイドル状態としてマークせずに(ステップ 4 をスキップして)ページのライトバックを続行します。すべてのアイドル ページが書き戻されたら、すべての ZRAM ページを再びアイドルとしてマークし、ステップ 2 に戻ります。ZRAM ライトバックが無効になっている場合、再圧縮アイドル期間後に ZRAM 再圧縮が発生すると、
mmdはすべての ZRAM ページをアイドル状態としてマークします。
トラブルシューティングと検証のガイダンス
次の検証手順とトラブルシューティングの手順を使用して、mmd と ZRAM のオペレーションを検証して診断します。
ZRAM の設定を検証する
mmd が起動時に ZRAM を正常に構成したことを確認するには:
アクティブな圧縮アルゴリズムとディスクサイズを確認します。
cat /sys/block/zram0/comp_algorithm cat /sys/block/zram0/disksizemmdシステム プロパティと実行中のサービスの状態を確認します。getprop | grep mmd.zram dumpsys -l | grep mmd
ZRAM のメンテナンスと書き戻しを検証する
ZRAM の書き戻しと再圧縮のメンテナンス タスクが機能していることを確認します。
バッキング ブロック デバイスのステータスを確認します。
cat /sys/block/zram0/bd_stat/sys/block/zram0/mm_statをモニタリングして再圧縮の効率を確認します。圧縮されたデータサイズの変更は、メンテナンス サイクル後に表示されます。
プロセスごとの書き込みを検証する
プロセスごとのライトバックが機能していることを検証するには、次の方法を使用できます。
adb logcat -s mmdで、書き戻しログが成功したか、失敗したかの診断を確認します。
よくある問題と診断
ユーザーが直面する可能性のある一般的なエラー状況を以下に示します。
WritebackDailyLimitExceeded: このエラーは、mmd.zram.writeback.max_bytes_per_day割り当てに達したことを示します。この場合、24 時間のローリング ウィンドウが進むまで、mmdはアイドル状態のライトバックを一時停止します。Process prefetch or writeback failed: このエラーは、ioctl が失敗したときに logcat で確認できます。一般的な原因は次のとおりです。EBADFまたはESRCH:mmdがpidfdをカーネルにディスパッチする前に、ターゲット プロセスが終了しました。ENOSPC: バッキング ストレージ パーティションがいっぱいであるか、ループ デバイスキューが使い果たされています。
- ZRAM が設定されていない:
mmdが起動時に ZRAM の設定に失敗した場合、従来のswapon_allまたはベンダーの init スクリプトがmmdの実行前に/dev/block/zram0をロックしたことが原因である可能性があります。