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 裝置,則會指定minimumRequiredMemTotal13312(13 GiB) 而非16384(16 GiB),而 12 GB 裝置的層級則會指定10240(10 GiB)。這可確保每個裝置都符合預期層級,無論板級區隔差異為何。memVisible套用至可見程序的軟性記憶體限制 (
memory.high),以 MiB 為單位。memNotVisible套用至不可見程序的軟性記憶體限制 (
memory.high),以 MiB 為單位。swapVisible套用至可見程序的交換空間限制 (
memory.swap.max),以 MiB 為單位。swapNotVisible以 MiB 為單位,套用至不可見程序的交換空間限制 (
memory.swap.max)。
標準平台限制
在 Android 17 以上版本中,平台會在 /system/etc/memory-limiter-config.xml 中指定標準記憶體和交換限制,適用於常見的實體 RAM 層級:
| 目標實體 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 容量進行校準:
- 可見程序:分配約 1/2 至 2/3 的實體 RAM 總量,支援作用中的 UI、合成和算繪工作負載。
- 不可見的程序:為背景工作分配約 1/4 至 1/3 的實體 RAM 總量。
普遍適用:限制會統一套用至裝置上的所有應用程式程序 (UID >= 10000),包括預先安裝的系統和 OEM 應用程式。記憶體限制器不支援允許清單,因此無法排除特定應用程式,確保所有應用程式的記憶體管理機制公平公正。
缺少執行階段查詢 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
手動覆寫只會在特定程序例項的生命週期內生效。 如果程序重新啟動,系統會根據程序狀態還原為標準限制。
手動覆寫值不得超過實體系統限制。