Android 17 以降では、Linux cgroup v2 を使用してアプリケーション プロセスのメモリ使用量をモニタリングして制限するシステム サービスであるメモリ リミッターがサポートされています。メモリ リミッターは、個々のアプリが過剰にシステムメモリを消費するのを防ぎ、システム全体のメモリ負荷を軽減し、重要なプロセスが強制終了(OOM)されるのを防ぎます。
メカニズム
メモリ リミッターは Activity Manager Service(AMS)と統合して、プロセスのライフサイクル イベントと状態の変化を追跡します。メモリ リミッターは、Linux カーネルの cgroup v2 ファイル システムを使用してメモリ上限を適用します。
メモリ リミッターを使用するには、デバイスのカーネルが cgroup v2 と memory コントローラをサポートしている必要があります。このサービスは、次の属性に依存しています。
memory.high- ソフトリミット。この上限を超えると、プロセスがスロットリングされ、カーネルがプロセスからメモリを回収しようとします。
memory.swap.max- プロセスが使用できるスワップ領域の量を制限します。
アプリへの影響
メモリ上限を超えないアプリは、メモリ リミッターの影響を受けません。
アプリが memory.high 上限を超えると、カーネルはアプリのファイル バックアップ メモリを削除し、匿名メモリをスワップして、アプリを上限内に維持します。削除とスワップの結果、アプリの実行速度が低下する可能性があります。
極端な場合、アプリが匿名メモリの割り当てを続け、デバイスのスワップ領域が不足すると、アプリがメモリを割り当てられず、クラッシュする可能性があります。
プロセスのモニタリング
メモリ リミッターは、デフォルトでアプリ プロセス(UID >= 10000)をモニタリングします。システム プロセスは、コア システムの安定性を検証するために、通常は除外されます。
メモリ リミッターは、プロセスの状態に基づいてメモリ上限を割り当てます。
表示プロセス は、ユーザーに UI を表示できる状態です。 UI を表示している間、プロセスはより大きな RAM ワーキング セットを使用することが想定されるため、より大きなメモリ上限が割り当てられます。
非表示プロセス は、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 への異常のロギングなどのデバッグ アクションをトリガーできます。
構成
メモリ リミッターは、vendor パーティションにある XML ファイルを使用して構成します。構成により、デバイスの特定のメモリ制約に基づいて絶対メモリ上限を調整できます。
ファイルパス:
/vendor/etc/memory-limiter-config.xmlデフォルト構成: 構成ファイルが見つからない場合、読み取り不能な場合、無効な場合、メモリ リミッターは無効になります。
XML 形式
構成ファイルは、memory-limiter-config.xsd で定義されたスキーマに従います。このファイルでは、複数の上限セットを定義できます。サービスは、デバイスで使用可能な RAM に基づいて最適な一致を選択します。すべてのメモリ値は、メビバイト(MiB)単位で定義されます。
<MemoryLimiterConfig>
<version>1</version>
<configList>
<limitSet>
<!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- 構成バージョンを識別する正の整数。1 である必要があります。
minimumRequiredMemTotal- この上限セットが有効になるために必要な最小のシステムメモリ。
memVisible- 表示プロセスに許可されるメモリ上限(
memory.high)。 memNotVisible- 非表示プロセスに許可されるメモリ上限(
memory.high)。 swapVisible- 表示プロセスに許可されるスワップ上限(
memory.swap.max)。 swapNotVisible- 非表示プロセスに許可されるスワップ上限(
memory.swap.max)。
デバイスのメモリ上限に関するガイドライン
デバイスのメモリ上限を構成する際は、次のガイドラインを考慮してください。
ハードウェア機能に合わせて上限を調整する: デバイス OEM は、デバイスのハードウェア機能に合わせて上限を設定できます。Android では、次の範囲をおすすめします。
- 表示プロセス: 物理 RAM の合計の 1/2 以上 2/3 以下。
- 非表示プロセス: 物理 RAM の合計の 1/4 ~ 1/3。 OEM は、デバイスの機能とユースケースに基づいて異なる判断を行うことができます。
アプリのランタイム API がない: Android 17(SDK 37)の時点では、アプリにランタイムでメモリ上限をクエリする API がありません。OEM はこの点を考慮し、上限を低く設定しすぎないようにして、合理的なユースケースでアプリが上限に達しないようにする必要があります。
ユニバーサル構成: 上限は、プリインストール アプリを含む、デバイス上のすべてのアプリ プロセスに適用されます。特定の上限を免除する許可リストはありません。
構成を変更する
システム全体の上限を変更する手順は次のとおりです。
/vendor/etc/memory-limiter-config.xmlを変更します。- 変更を有効にするには、デバイスを再起動するか、
system_serverを再起動します。
シェルコマンド
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- リミッターがプロセスをアクティブに監視しているかどうかを示します。
visibleMem、notVisibleMem- 各状態の計算された絶対メモリ上限を示します。
events- プロセスが上限を超えた回数。
processes- モニタリング対象のプロセスの数。
無視
ignore サブコマンドは、UID またはすべてのプロセスを一時的に制限から除外します。このアクションは、パフォーマンス テストや、特定のアプリが上限を超えることを許可する場合に便利です。
adb shell am memory-limiter ignore 10087 // Ignore a specific UIDadb shell am memory-limiter ignore all // Ignore all processes (effectively disables limiting)adb shell am memory-limiter ignore none // Resume normal operation
手動
manual サブコマンドは、特定のプロセス(プロセス ID または PID)の計算された上限を、メガバイト(MB)単位のカスタム絶対値でオーバーライドします。
adb shell am memory-limiter manual 1234 1024 // Set a 1024 MB limit for PID 1234adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234
手動オーバーライドは、プロセスのライフサイクルにのみ適用されます。プロセスが再起動すると、状態に基づいてデフォルトの上限に戻ります。