記憶體限制程式

Android 17 以上版本包含記憶體限制器,這項系統服務會使用 Linux cgroup v2 監控及限制應用程式程序的記憶體用量。記憶體限制器可防止個別應用程式消耗過多系統記憶體,進而降低整體記憶體壓力,並防止系統在記憶體不足時,過度終止重要的前景和快取程序。

機制

記憶體限制器與活動管理工具服務 (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。Carve-out 會為 GPU、基頻數據機、相機 ISP、安全執行環境和管理程序等專用硬體元件保留 RAM,通常會耗用 500 MiB 到超過 2 GiB 的實體記憶體。

由於 MemTotal 低於裝置標榜的實體 DRAM,minimumRequiredMemTotal 門檻會包含這些預留空間。舉例來說,指定 16 GB 裝置的設定會指定 14336 (14 GiB) 的下限,而非 16384 (16 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) 範圍間隔,在 /system/etc/memory-limiter-config.xml 中指定常見實體 RAM 級別的標準記憶體和交換限制:

目標實體 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 容量調整:

    • 可見程序:分配約 1/2 至 2/3 的實體 RAM 總量,支援活躍的 UI、組合和算繪工作負載。
    • 不可見程序:為背景工作分配約 1/4 到 1/3 的實體 RAM 總量。
  • 全面適用:限制會統一套用至裝置上的所有應用程式程序 (UID >= 10000),包括預先安裝的系統和原始設備製造商 (OEM) 應用程式。記憶體限制器不支援許可清單,無法豁免特定應用程式,確保所有應用程式都能公平管理記憶體。如要為重要的互動式 Daemon 設定自我調整 memory.min 保護下限,並為個別系統應用程式和服務設定更嚴格、依工作負載校準的 memory.high 上限,請參閱「系統記憶體預算」。

  • 缺少執行階段查詢 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

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

手動覆寫僅適用於該特定程序執行個體的生命週期。如果程序重新啟動,則會根據其狀態返回標準限制。

手動覆寫值不得超過實體系統限制。