記憶體限制程式

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
指出記憶體限制器是否正在監控程序。
visibleMemnotVisibleMem
目前對每個可見度狀態強制執行的計算絕對記憶體限制 (memory.high)。
visibleSwapnotVisibleSwap
目前針對各曝光度狀態強制執行的計算絕對交換限制 (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

手動覆寫只會在特定程序例項的生命週期內生效。 如果程序重新啟動,系統會根據程序狀態還原為標準限制。

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