メモリ管理デーモン

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 を使用して行われます。

zram-maintenance

図 1.ZRAM メンテナンスのスケジュール設定フロー。

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

プロセスごとの書き戻し

プロセスごとのメモリの強制排除は、ActivityManagerService によって com.android.server.am.CachedAppOptimizer を使用して管理されます。

mmd-writeback

図 2. mmd プロセスごとの書き戻しフロー。

プロセスがバックグラウンド キャッシュ状態に移行すると、ActivityManager はメモリの圧縮を行います。 プロセスのローメモリ強制終了がユーザーに表示される場合(プロセスがアクティビティをホストしている場合)、ZRAM のプロセスごとの書き戻しによってプロセスのメモリ使用量がほぼゼロになる場合は、システムは次の手順を行います。

  1. 圧縮後、CachedAppOptimizer は遅延メッセージ(ZRAM_WRITEBACK_MSG)を内部圧縮ハンドラに送信します(mZramWritebackWaitSeconds だけ遅延します)。
  2. 遅延が終了すると、ActivityManager は安全なプロセス ファイル記述子 pidfd を開きます。
  3. システム サーバーは mmd.asyncWritebackProcessZramMemory(pfd, callback) を呼び出します。
  4. mmd はプロセスごとの書き戻し ioctl を実行し、IMmdProcessWritebackCallback を使用してレポートを返します。成功した場合、 ActivityManager はプロセス レコードにフラグを設定し(setIsZramWrittenBack(app, true))、プロセスの oom_score_adj をブーストし、指標を FrameworkStatsLog.ZRAM_WRITEBACK_EVENT に記録します。

プロセスごとのプリフェッチ

ユーザーが以前にキャッシュされたアプリを再起動すると(UNFREEZE_REASON_ACTIVITY によりフリーズ解除)、ActivityManager はバッキング ストレージからの大きなページフォルトによって発生するアプリの起動レイテンシを最小限に抑えます。

  1. CachedAppOptimizer はフリーズ解除イベントをインターセプトし、prefetchZram(app) を呼び出します。
  2. システム サーバーは、mmd.asyncPrefetchProcessZramMemory(pfd) を使用して、Binder 経由でアプリの pidfd をディスパッチします。mmdZRAM_ANDROID_IOC_PROCESS_PREFETCH ioctl を発行し、アプリのメイン UI スレッドが初期化されている間に、スワップされたページを非同期で RAM にプリフェッチするようにカーネルに指示します。

メンテナンスと事後処理タスクの概要

このセクションでは、mmd がスワップ領域とシステム メモリを最適化するために実行するバックグラウンド メンテナンス オペレーションと事後処理タスクについて説明します。

mmd でのメンテナンス

mmd では、メンテナンスとは、アクティブなユーザーのフォアグラウンド パフォーマンスに影響を与えることなく、スワップ領域と物理メモリの使用率を最適化する、スケジュールされたバックグラウンド メンテナンス スイープを指します。 継続的で同期的なスイープ(CPU のウェイクアップと UI のジャンクが発生する)を実行する代わりに、メンテナンスは非同期で行われます。

  1. system_server は、Binder 経由で doZramMaintenanceAsync() を定期的に起動します。

  2. mmd は、リクエストをバックグラウンド作業キュー LowPrioWorkItem::ZramMaintenance に配置します。

  3. mmd には、優先度の高いキューと優先度の低いキューの両方を管理する単一のワーカー スレッドがあります。優先度の高い作業項目(プロセスごとのプリフェッチなど)が最初に処理され、優先度の低い作業項目をプリエンプトできます。メンテナンスとプロセスごとの書き戻しは、優先度の低い作業項目として動作します。ポップアップすると、ワーカー スレッドは次の 2 つの主要なメンテナンス オペレーションを順番に実行します。

    • ZRAM の再圧縮: 既存のスワップページをスイープし、より高い比率の二次圧縮アルゴリズム(zstd など)を使用してアイドル状態のページを再圧縮します。

    • ZRAM の書き戻し: アイドル状態のページをスキャンし、/data のファイルからバッキング フラッシュ ストレージのループ デバイスに RAM から完全に強制排除します。

ZRAM の事後処理タスク

Linux カーネル ZRAM モジュールと mmd アーキテクチャでは、事後処理タスクは、カーネルの標準の再利用パス(kswapd または圧縮)によってすでにスワップアウトされたメモリページに適用される非同期変換です。

ページが最初にスワップアウトされるとき、システムは速度を優先します。高速なプライマリ圧縮アルゴリズム(lz4 など)を使用し、圧縮されたページを RAM に保存します。ただし、時間が経つにつれて、スワップされたページの多くはコールドまたはアイドル状態になります。たとえば、数時間再開されないバックグラウンド キャッシュ アプリなどです。コールドページを高速で圧縮率の低い ZRAM に残すのは非効率的です。

事後処理パイプライン

mmd は、これらのページを最適化するために、マルチステージの事後処理ライフサイクルを実装します。

mmd-page-lifecycle

図 3.mmd ページのライフサイクル。

  1. ステージ 1: 最初のスワップアウト(高速圧縮): メモリは、最初に kswapd またはアプリの圧縮によって再利用されます。通常、この最初の再利用は、lz4 などの高速圧縮アルゴリズムを使用して実行され、コンテンツは RAM に保存されます。

  2. ステージ 2: アイドル状態のマーキング(エージングとトラッキング): mmd アイドル トラッキングは、カーネル メモリ トラッキング(CONFIG_ZRAM_TRACK_ENTRY_ACTIME)にアクセスするか、ソフトウェア アイドル マーカーを使用して、ページが操作されていない期間をトラッキングします。

  3. ステージ 3: 事後処理 1 - 再圧縮(メモリ内再利用): 再圧縮アイドル時間に達したページ(min_idle_seconds から max_idle_seconds)は再圧縮されます。mmd/sys/block/zram0/recompress に書き込み、lz4 ページを解凍して zstd を使用して再圧縮するようにカーネルに指示します。これにより、フラッシュ書き込みの摩耗を発生させることなく、物理 RAM を再利用できます。

  4. ステージ 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.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

既存の ZRAM 設定の非推奨

Android では swapon_all を使用して ZRAM とディスクベースのスワップ領域を設定できますが、ZRAM の管理には mmd を使用することをおすすめします。これにより、構成が簡単になり、ZRAM の再圧縮などの高度な機能を使用できます。

mmd.zram.enabledmmd ZRAM 設定が有効になっている場合:

  • swapon_all 実装での ZRAM 設定は no-op になります。
  • オーバーレイ config.xml ファイルの config_zramWritebackro.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 ページをトラッキングします。

  1. mmd の起動時に、すべての ZRAM ページをアイドル状態としてマークします。

  2. 必要なバックオフ期間が経過するまで、次の ZRAM メンテナンスをスキップします。

  3. ZRAM の書き戻しまたはアイドル状態のページの再圧縮を行います。書き戻しの上限によりアイドル状態のページが残っている場合、mmd は次のメンテナンスで新しいページをアイドル状態としてマークせずに(ステップ 4 をスキップして)ページの書き戻しを続行します。

  4. すべてのアイドル状態のページが書き戻されたら、すべての ZRAM ページを再度アイドル状態としてマークし、ステップ 2 に戻ります。ZRAM の書き戻しが無効になっている場合、mmd は再圧縮アイドル時間の経過後に ZRAM の再圧縮が行われると、すべての ZRAM ページをアイドル状態としてマークします。

トラブルシューティングと検証のガイダンス

次の検証手順とトラブルシューティング手順を使用して、mmd と ZRAM のオペレーションを検証して診断します。

ZRAM 設定を検証する

mmd が起動時に ZRAM を正常に構成したことを確認するには:

  1. アクティブな圧縮アルゴリズムとディスクサイズを確認します。

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. mmd システム プロパティと実行中のサービスの状態を確認します。

    getprop | grep mmd.zram
    dumpsys -l | grep mmd
    

ZRAM メンテナンスと書き戻しを検証する

ZRAM の書き戻しと再圧縮のメンテナンス タスクが機能していることを確認します。

  1. バッキング ブロック デバイスのステータスを確認します。

    cat /sys/block/zram0/bd_stat
    
  2. /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: mmdpidfd をカーネルにディスパッチする前に、ターゲット プロセスが終了しました。
    • ENOSPC: バッキング ストレージ パーティションがいっぱいになっているか、ループ デバイスキューが使い果たされています。
  • ZRAM が設定されていない: 起動時に mmd が ZRAM を構成できない場合は、レガシー swapon_all またはベンダーの init スクリプトが mmd の実行前に /dev/block/zram0 をロックしたことが原因である可能性があります。