Android 17 以降には、Linux cgroup v2 を使用してアプリケーション プロセスのメモリ使用量をモニタリングして制限するシステム サービスであるメモリリミッターが搭載されています。Memory Limiter は、個々のアプリがシステムメモリを不均衡に消費するのを防ぎます。これにより、全体的なメモリ負荷が軽減され、重要なフォアグラウンド プロセスやキャッシュされたプロセスがメモリ不足により強制終了されるのを防ぎます。
メカニズム
メモリリミッターは、アクティビティ マネージャー サービス(AMS)と統合され、プロセスのライフサイクル イベントと状態の変化を追跡します。Memory Limiter は、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 の上限を超えると、Memory Limiter がイベントを検出し、Memory Profiler のキャプチャや 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 未満のデバイスの場合、Memory Limiter は制限を適用せず、プロセスは制限されません。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) |
メモリ上限の原則
Memory Limiter の構成は、次のプラットフォーム原則に基づいて設計されています。
エコシステムの一貫性とアプリの互換性: デバイス間でメモリ上限を統一することで、Android エコシステム全体でアプリのパフォーマンスを予測可能にできます。アプリは標準的なメモリ要件を想定して開発、テストされます。プラットフォームの制限を統一することで、予期しないスロットリングや早期終了を回避できます。
比例的なリソース割り当て: プラットフォームの上限は、物理 RAM 容量に応じて調整されます。
- 可視プロセス: アクティブな UI、コンポジション、レンダリング ワークロードをサポートするために、物理 RAM の合計の約 1/2 ~ 2/3 を割り当てます。
- 不可視プロセス: バックグラウンド タスク用に、物理 RAM の合計の約 4 分の 1 から 3 分の 1 を割り当てます。
普遍的な適用性: 制限は、プリインストールされたシステムアプリや OEM アプリを含む、デバイス上のすべてのアプリケーション プロセス(UID >= 10000)に一律に適用されます。メモリリミッターは、特定のアプリを除外する許可リストをサポートしていないため、すべてのアプリで公平なメモリ ガバナンスが確保されます。重要なインタラクティブ デーモンに自動調整の
memory.min保護フロアを構成し、個々のシステムアプリとサービスにワークロードに合わせて調整されたより厳格なmemory.high上限を構成するには、システムメモリの予算をご覧ください。ランタイム クエリ API の欠如: Android 17 以降では、アプリは実行時に割り当てられたメモリ上限をプログラムでクエリできません。標準のプラットフォーム制限は、適切なアプリが通常の利用時に制約なく動作できるように、十分な余裕を持って定義されています。
カーネルの再利用とスワップのダイナミクス: アプリプロセスが
memory.highの上限に近づくと、Linux カーネルは、そのプロセス cgroup を対象にメモリの再利用を行います。これには、アクティブでないファイルベースのページをドロップし、コールドな匿名ページを ZRAM にスワップすることが含まれます。これにより、グローバルなメモリ負荷をかけることなく、システムの流動性を維持できます。
シェルコマンド
am memory-limiter コマンドを使用すると、デベロッパーとシステム インテグレータは、開発、テスト、デバッグのために実行時にサービスを操作できます。
am memory-limiter <SUB-COMMAND>ステータス
status サブコマンドは、Memory Limiter の動作ステータスとアクティブな指標を報告します。
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- 各可視性状態に現在適用されている、計算された絶対メモリ上限(
memory.high)。 visibleSwap、notVisibleSwap- 各可視性状態に現在適用されている、計算された絶対スワップ上限(
memory.swap.max)。 events- プロセスが割り当てられた上限を超えた回数。
processes- モニタリング対象のプロセスの現在の数。
ignore
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
手動オーバーライドは、その特定のプロセス インスタンスの存続期間にのみ適用されます。プロセスが再起動すると、状態に基づいて標準の制限に戻ります。
手動オーバーライドは、物理システムの上限を超えることはできません。