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 以降では、メモリ制限機能はシステム パーティションに標準のプラットフォーム デフォルト構成ファイルを提供し、vendor パーティションでデバイス固有のオーバーライドをサポートしています。
- システムのデフォルト構成:
/system/etc/memory-limiter-config.xml - ベンダーのオーバーライド(省略可):
/vendor/etc/memory-limiter-config.xml
メモリ制限機能は、最初に /vendor/etc/memory-limiter-config.xml を確認します。
存在する場合はベンダー構成を使用します。存在しない場合は、/system/etc/memory-limiter-config.xml のデフォルトのシステム構成に戻ります。
XML 形式
構成ファイルは、memory-limiter-config.xsd で定義されたスキーマに従います。このファイルでは、使用可能なメモリ順に並べられた複数の上限セットが定義されています。このサービスは、デバイスの使用可能な RAM に基づいて、最も一致する上限セットを選択します。すべてのメモリ値は MiB 単位で指定されます。
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<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 から 1 GiB 以上の物理メモリを消費します。MemTotalはデバイスの広告されている物理 DRAM よりも小さいため、minimumRequiredMemTotalのしきい値には、これらのカーブアウトのマージンが含まれています。 たとえば、16 GB デバイスをターゲットとする構成では、16384(16 GiB)ではなく13312(13 GiB)のminimumRequiredMemTotalを指定します。12 GB デバイスの階層では、10240(10 GiB)を指定します。これにより、ボードレベルのカーブアウトのバリエーションに関係なく、各デバイスが目的の階層に一致します。memVisible表示プロセスに適用される MiB 単位のソフトメモリ上限(
memory.high)。memNotVisible非表示プロセスに適用される MiB 単位のソフトメモリ上限(
memory.high)。swapVisible表示プロセスに適用される MiB 単位のスワップ領域上限(
memory.swap.max)。swapNotVisible非表示プロセスに適用される MiB 単位のスワップ領域上限(
memory.swap.max)。
標準プラットフォームの上限
Android 17 以降では、プラットフォームは一般的な物理 RAM 階層全体で、/system/etc/memory-limiter-config.xml に標準のメモリ上限とスワップ上限を指定します。
| ターゲットの物理 RAM |
必要な最小値 MemTotal (minimumRequiredMemTotal) |
表示メモリ上限 (memVisible) |
非表示メモリ上限 (memNotVisible) |
表示スワップ上限 (swapVisible) |
非表示スワップ上限 (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13,312 MiB(13 GiB) | 10,240 MiB(10 GiB) | 5,120 MiB(5 GiB) | 5,120 MiB(5 GiB) | 5,120 MiB(5 GiB) |
| 12 GB | 10,240 MiB(10 GiB) | 8,192 MiB(8 GiB) | 4,096 MiB(4 GiB) | 4,096 MiB(4 GiB) | 4,096 MiB(4 GiB) |
| 8 GB | 6,656 MiB(6.5 GiB) | 5,120 MiB(5 GiB) | 3,072 MiB(3 GiB) | 3,072 MiB(3 GiB) | 3,072 MiB(3 GiB) |
| 6 GB | 4,608 MiB(4.5 GiB) | 4,096 MiB(4 GiB) | 2,048 MiB(2 GiB) | 2,048 MiB(2 GiB) | 2,048 MiB(2 GiB) |
| 4 GB | 3,072 MiB(3 GiB) | 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- メモリ制限機能がプロセスを積極的に監視しているかどうかを示します。
visibleMemとnotVisibleMem- 現在、可視性の状態ごとに適用されている絶対メモリ上限(
memory.high)が計算されます。 visibleSwapとnotVisibleSwap- 現在、可視性の状態ごとに適用されている絶対スワップ上限(
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)で指定された特定のプロセスの計算された上限を、バイト単位のカスタム絶対値でオーバーライドします。値は整数にする必要がありますが、MB 接尾辞を含めて MiB を示すか、GB 接尾辞を含めて GiB を示すことができます。
# 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
手動オーバーライドは、その特定のプロセス インスタンスの有効期間にのみ適用されます。 プロセスが再起動すると、その状態に基づいて標準の上限に戻ります。
手動オーバーライドは、物理システムの上限を超えることはできません。