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 メンテナンスは、ActivityManagerService によって com.android.server.memory.ZramMaintenance を使用して行われます。

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

図 2. mmd プロセスごとの書き戻しフロー。
プロセスがバックグラウンド キャッシュ状態に移行すると、ActivityManager はメモリの圧縮を行います。 プロセスのローメモリ強制終了がユーザーに表示される場合(プロセスがアクティビティをホストしている場合)、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 の書き戻し: アイドル状態のページをスキャンし、
/dataのファイルからバッキング フラッシュ ストレージのループ デバイスに RAM から完全に強制排除します。
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 カーネル構成を有効にする必要があります。CONFIG_ZRAM_TRACK_ENTRY_ACTIME は、GKI カーネル 6.18 以降でデフォルトで有効になっています。以前のカーネルでは、メモリのオーバーヘッドが発生し、デフォルトでは有効になっていません。
カーネル構成が有効になっていない場合、mmd ZRAM メンテナンスはソフトウェア代替ロジックにフォールバックして、アイドル状態の ZRAM ページをトラッキングします。
mmdの起動時に、すべての ZRAM ページをアイドル状態としてマークします。必要なバックオフ期間が経過するまで、次の ZRAM メンテナンスをスキップします。
ZRAM の書き戻しまたはアイドル状態のページの再圧縮を行います。書き戻しの上限によりアイドル状態のページが残っている場合、
mmdは次のメンテナンスで新しいページをアイドル状態としてマークせずに(ステップ 4 をスキップして)ページの書き戻しを続行します。すべてのアイドル状態のページが書き戻されたら、すべての ZRAM ページを再度アイドル状態としてマークし、ステップ 2 に戻ります。ZRAM の書き戻しが無効になっている場合、
mmdは再圧縮アイドル時間の経過後に ZRAM の再圧縮が行われると、すべての 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の割り当てに達したことを示します。このエラーが発生すると、mmdは 24 時間のローリング ウィンドウが進むまでアイドル書き戻しを一時停止します。Process prefetch or writeback failed: ioctl が失敗すると、このエラーが logcat に表示されます。一般的な原因は次のとおりです。EBADFまたはESRCH:mmdがpidfdをカーネルにディスパッチする前に、ターゲット プロセスが終了しました。ENOSPC: バッキング ストレージ パーティションがいっぱいになっているか、ループ デバイスキューが使い果たされています。
- ZRAM が設定されていない: 起動時に
mmdが ZRAM を構成できない場合は、レガシーswapon_allまたはベンダーの init スクリプトがmmdの実行前に/dev/block/zram0をロックしたことが原因である可能性があります。