メモリ リミッター

Android 17 以降には、Linux cgroup v2 を使用してアプリ プロセスのメモリ使用量をモニタリングして制限するシステム サービスであるメモリ リミッターが含まれています。メモリ制限ツールは、個々のアプリがシステムメモリを過剰に消費することを防ぎ、メモリ全体の負荷を軽減し、重要なフォアグラウンド プロセスとキャッシュ プロセスの低メモリでの強制終了を回避します。

メカニズム

メモリ制限機能は、Activity Manager Service(AMS)と統合して、プロセスのライフサイクル イベントと状態の変化をトラッキングします。メモリ制限ツールは、Linux カーネルの cgroup v2 階層を使用してメモリ上限を適用します。

メモリ制限機能をサポートするには、デバイスのカーネルで cgroup v2 と memory コントローラを有効にする必要があります。このサービスは、特に次の属性に依存しています。

memory.high
ソフトリミット。この値を超えると、プロセスがスロットリングされ、カーネルがそのプロセスからプロアクティブなページ再利用を試みます。
memory.swap.max
プロセスで使用できるスワップ領域(ZRAM など)の最大量を制限します。

アプリへの影響

メモリ制限内で動作するアプリは、メモリ制限機能の影響を受けません。

アプリが memory.high の上限を超えると、カーネルはアプリのクリーンなファイル バックアップ メモリを削除し、アプリを上限内に収めるために非アクティブな匿名メモリをスワップアウトします。このページ エビクションとスワップ アクティビティの結果、アプリで一時的な実行スロットリングが発生する可能性がありますが、アプリは引き続き実行されます。

極端な状況(スワップ領域が使い果たされた後もアプリが匿名メモリを割り当て続ける制御不能なメモリリークなど)では、アプリはメモリの割り当てに失敗し、終了します。

プロセスのモニタリング

メモリ制限ツールは、デフォルトでアプリプロセス(UID >= 10000)をモニタリングします。コア システム プロセスは、システムの安定性を維持するために除外されます。

メモリ リミッターは、プロセスの可視性状態に基づいてメモリ上限を割り当てます。

  • 可視プロセス: ユーザーに表示される UI(現在フォーカスされているアクティビティやアクティブなインタラクションなど)をホストするプロセス。UI の表示にはレンダリングとグラフィック パイプライン用のより大きなワーキング セットが必要になるため、可視プロセスにはより大きなメモリ上限が割り当てられます。

  • 非表示プロセス: UI を表示せずにバックグラウンドで処理を実行するプロセス(バックグラウンド サービスやブロードキャスト レシーバなど)。UI コンポーネントをレンダリングしないため、より厳しい制限が適用されます。

次の表は、特定のプロセス状態とメモリ制限の分類をマッピングしたものです。

プロセスの状態メモリ上限
PERSISTENT制限なし
PERSISTENT_UI制限なし
TOP表示
BOUND_TOP表示
FOREGROUND_SERVICE非表示
BOUND_FOREGROUND_SERVICE非表示
IMPORTANT_FOREGROUND表示
IMPORTANT_BACKGROUND非表示
TRANSIENT_BACKGROUND非表示
BACKUP非表示
SERVICE非表示
RECEIVER非表示
TOP_SLEEPING表示
HEAVY_WEIGHT非表示
HOME非表示
LAST_ACTIVITY非表示
CACHED_ACTIVITYキャッシュ済み
CACHED_ACTIVITY_CLIENTキャッシュ済み
CACHED_RECENTキャッシュ済み
CACHED_EMPTYキャッシュ済み

キャッシュされた状態では、プロセスがフリーズされ、最大限に再利用されます。

プロセスが割り当てられた memory.high 上限を超えると、メモリ リミッターがイベントを検出し、メモリ プロファイルのキャプチャや statsd への異常のロギングなどの診断アクションをトリガーできます。

構成

Android 17 以降では、メモリ制限は /system/etc/memory-limiter-config.xml のシステム パーティションにある標準プラットフォーム構成ファイルを使用して構成されます。

XML 形式

構成ファイルは、memory-limiter-config.xsd で定義されたスキーマに準拠しています。このファイルは、使用可能なメモリで順序付けされた複数の上限セットを定義します。サービスは、デバイスの使用可能な RAM に基づいて、一致する上限セットのうち最も高いものを選択します。メモリ値はすべて MiB(メビバイト)単位で指定します。

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM boundaries account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
構成バージョンを識別する正の整数。これは 1 にする必要があります。
minimumRequiredMemTotal

この上限セットを適用するために必要な最小システム MemTotal(MiB 単位、下限を含む)。サービスは、これを /proc/meminfo でカーネルによって報告された使用可能なメモリの合計と比較します。

メモリのカーブアウトと境界を理解する: カーネルの MemTotal 値は、ハードウェアとファームウェアのメモリ カーブアウトが差し引かれた後にオペレーティング システムがアクセスできる物理 DRAM を反映します。カーブアウトは、GPU、ベースバンド モデム、カメラ ISP、セキュア実行環境、ハイパーバイザなどの特殊なハードウェア コンポーネント用に RAM を予約します。通常、500 MiB から 2 GiB 以上の物理メモリを消費します。

MemTotal はデバイスの公表されている物理 DRAM よりも低いため、minimumRequiredMemTotal のしきい値にはこれらのカーブアウトのマージンが含まれます。たとえば、16 GB デバイスを対象とする構成では、16384(16 GiB)ではなく 14336(14 GiB)の下限を指定し、12 GB デバイスの階層では 9216(9 GiB)の下限を指定します。これにより、ボードレベルの切り出しのバリエーションに関係なく、各デバイスが意図した階層に一致します。

maximumAllowedMemTotal

この上限セットが適用されるシステムの最大 MemTotal(MiB 単位、上限は含まない)。MemTotal がこの上限以上の場合、設定された上限は適用されません。18,432 MiB 以上のデバイス、または 3,200 MiB 未満のデバイスの場合、メモリ リミッターは制限を適用せず、プロセスは制約を受けません。

memVisible

可視プロセスに適用されるソフトメモリ上限(memory.high)(MiB 単位)。

memNotVisible

非表示のプロセスに適用されるソフトメモリ上限(memory.high)(MiB 単位)。

swapVisible

表示されるプロセスに適用されるスワップ領域の上限(memory.swap.max)(MiB 単位)。

swapNotVisible

非表示のプロセスに適用されるスワップ領域の上限(memory.swap.max)(MiB 単位)。

標準プラットフォームの上限

Android 17 以降では、プラットフォームは [floor, ceiling) 範囲の間隔を使用して、一般的な物理 RAM ティア全体で /system/etc/memory-limiter-config.xml の標準メモリとスワップの制限を指定します。

物理 RAM のターゲット MemTotal の範囲 [下限, 上限)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
可視メモリ上限
(memVisible)
非表示メモリ上限
(memNotVisible)
表示可能なスワップ上限
(swapVisible)
非表示の交代上限
(swapNotVisible)
16 GB [14336, 18432) MiB 10,240 MiB(10 GiB) 5,120 MiB(5 GiB) 5,120 MiB(5 GiB) 5,120 MiB(5 GiB)
12 GB [9216, 14336) MiB 8,192 MiB(8 GiB) 4,096 MiB(4 GiB) 4,096 MiB(4 GiB) 4,096 MiB(4 GiB)
8 GB [6800, 9216) MiB 5,120 MiB(5 GiB) 3,072 MiB(3 GiB) 3,072 MiB(3 GiB) 3,072 MiB(3 GiB)
6 GB [4800, 6800) MiB 4,096 MiB(4 GiB) 2,048 MiB(2 GiB) 2,048 MiB(2 GiB) 2,048 MiB(2 GiB)
4 GB [3200, 4800) MiB 2,048 MiB(2 GiB) 1,024 MiB(1 GiB) 1,024 MiB(1 GiB) 1,024 MiB(1 GiB)

メモリ上限の原則

メモリ制限機能の構成は、次のプラットフォームの原則に基づいて設計されています。

  • エコシステムの整合性とアプリの互換性: デバイス間でメモリ上限を統一することで、Android エコシステム全体でアプリのパフォーマンスを予測できるようになります。アプリは標準的なメモリの想定に基づいて開発、テストされます。プラットフォームの上限を均一に維持することで、予期しないスロットリングや早期終了を回避できます。

  • 比例リソース割り当て: プラットフォームの上限は、物理 RAM 容量に応じて調整されます。

    • 可視プロセス: アクティブな UI、コンポジション、レンダリングのワークロードをサポートするために、物理 RAM の合計の約 1/2 ~ 2/3 を割り当てました。
    • 非表示のプロセス: バックグラウンド タスク用に、合計物理 RAM の約 1/4 ~ 1/3 が割り当てられます。
  • 普遍的な適用性: プリインストールされたシステムアプリや OEM アプリを含む、デバイス上のすべてのアプリプロセス(UID >= 10000)に制限が均一に適用されます。メモリ制限ツールは、特定のアプリを除外するための許可リストをサポートしていません。これにより、すべてのアプリで公平なメモリ ガバナンスが確保されます。

  • 実行時クエリ API の欠如: Android 17 以降では、アプリは実行時に割り当てられたメモリ上限をプログラムでクエリできません。標準プラットフォームの上限は、正常な動作をするアプリが通常の使用時に制約を受けないように、余裕のあるマージンで定義されています。

  • カーネルの回収とスワップのダイナミクス: アプリ プロセスが memory.high の上限に近づくと、Linux カーネルはメモリ回収をそのプロセス cgroup に限定します。これには、アクティブでないファイル バックアップ ページをドロップし、コールド匿名ページを ZRAM にスワップすることが含まれます。これにより、グローバル メモリの負荷をかけずにシステムの流動性を維持します。

シェルコマンド

am memory-limiter コマンドを使用すると、デベロッパーとシステム インテグレータは、開発、テスト、デバッグのために実行時にサービスを操作できます。

am memory-limiter <SUB-COMMAND>

ステータス

status サブコマンドは、メモリ制限ツールの運用ステータスとアクティブな指標をレポートします。

adb shell am memory-limiter status

出力例を次に示します。

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

出力のキーフィールドは次のとおりです。

monitoring
メモリ リミッターがプロセスをアクティブに監視しているかどうかを示します。
visibleMemnotVisibleMem
各可視性状態に現在適用されている計算された絶対メモリ上限(memory.high)。
visibleSwapnotVisibleSwap
各可視性状態に現在適用されている、計算された絶対スワップ上限(memory.swap.max)。
events
プロセスが割り当てられた上限を超えた回数。
processes
モニタリング対象のプロセスの現在の数。

無視

ignore サブコマンドは、特定の UID またはすべてのプロセスをメモリ制限から一時的に除外します。これは、パフォーマンスのベンチマーク、ストレステスト、メモリ動作の診断に役立ちます。

# Ignore a specific UID
adb shell am memory-limiter ignore 10087
# Ignore all processes (temporarily disables limiting)
adb shell am memory-limiter ignore all
# Resume normal limiting operation
adb shell am memory-limiter ignore none

手動

manual サブコマンドは、特定のプロセスの計算された上限をプロセス ID(PID)でオーバーライドし、カスタムの絶対値(バイト単位)を設定します。値は整数にする必要がありますが、MiB を示す MB 接尾辞または GiB を示す GB 接尾辞を含めることができます。

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

手動オーバーライドは、その特定のプロセス インスタンスの存続期間中のみ適用されます。プロセスが再起動すると、状態に基づいて標準の制限に戻ります。

手動オーバーライドは、物理システムの上限を超えることはできません。