記憶體限制程式

Android 17 以上版本支援記憶體限制器,這項系統服務會使用 Linux cgroup v2 監控及限制應用程式程序的記憶體用量。記憶體限制器可防止個別應用程式耗用過多系統記憶體,進而減輕系統整體記憶體壓力,並避免系統大量終止重要程序,導致記憶體不足 (OOM)。

機制

記憶體限制器會與活動管理服務 (AMS) 整合,追蹤程序生命週期事件和狀態變化。記憶體限制器會使用 Linux 核心 cgroup v2 檔案系統,強制執行記憶體限制。

如要使用記憶體限制器,裝置核心必須支援 cgroup v2 和 memory 控制器。這項服務特別依賴下列屬性:

memory.high
軟性限制。如果超出上限,系統就會限制程序,核心也會嘗試從中回收記憶體。
memory.swap.max
限制程序可用的交換空間量。

對應用程式的影響

如果應用程式未超出記憶體限制,就不會受到記憶體限制器影響。

當應用程式超過 memory.high 限制時,核心會逐出應用程式的檔案支援記憶體,並換出其匿名記憶體,讓應用程式維持在限制內。由於系統會逐出並交換應用程式,應用程式的執行速度可能會變慢。

在極端情況下,如果應用程式持續分配匿名記憶體,且裝置的交換空間不足,應用程式可能無法分配記憶體,因此很可能會當機。

程序監控

記憶體限制器預設會監控應用程式程序 (UID >= 10000)。系統程序通常會排除在外,以利驗證核心系統穩定性。

記憶體限制器會根據程序的狀態指派記憶體限制:

  • 可見的程序是指使用者可察覺的程序,例如前景活動、前景服務或其他可察覺的抖動狀態。

  • 不可見的程序是指使用者看不到或無法互動的背景程序。

下表將特定程序狀態對應至記憶體限制:

程序狀態記憶體限制
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

設定

使用 vendor 分區中的 XML 檔案設定記憶體限制器。您可以根據裝置的特定記憶體限制,調整絕對記憶體限制。

  • 檔案路徑: /vendor/etc/memory-limiter-config.xml

  • 預設設定:如果找不到設定檔,或設定檔無法讀取或無效,系統就會停用記憶體限制器。

XML 格式

設定檔會遵循 memory-limiter-config.xsd 中定義的結構。您可以在檔案中定義多個限制集,服務會根據裝置的可用 RAM 選擇最合適的限制集。所有記憶體值都以 MiB 為單位定義。

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <limitSet>
      <!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
識別設定版本的正整數。這必須是 1。
minimumRequiredMemTotal
這個限制生效所需的最低可用系統記憶體。
memVisible
可見程序可用的記憶體上限 (memory.high)。
memNotVisible
允許非顯示程序的記憶體上限 (memory.high)。
swapVisible
可見程序允許的交換限制 (memory.swap.max)。
swapNotVisible
允許不可見程序使用的交換空間上限 (memory.swap.max)。

修改設定

如要變更全系統限制,請按照下列步驟操作:

  1. 修改 /vendor/etc/memory-limiter-config.xml
  2. 重新啟動裝置或 system_server,變更才會生效。

殼層指令

您和開發人員可以使用 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
指出各個狀態的絕對記憶體限制。
events
程序超出限制的次數。
processes
受監控的程序數量。

忽略

ignore 子指令會暫時排除 UID 或所有程序,使其不受限制。這項動作適用於效能測試,或允許特定應用程式超出限制。

adb shell am memory-limiter ignore 10087  // Ignore a specific UID
adb shell am memory-limiter ignore all    // Ignore all processes (effectively disables limiting)
adb shell am memory-limiter ignore none   // Resume normal operation

手動

manual 子指令會以自訂的絕對值 (以 MB 為單位),覆寫特定程序 (依程序 ID 或 PID) 的計算限制:

adb shell am memory-limiter manual 1234 1024   // Set a 1024 MB limit for PID 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

手動覆寫只會套用至程序的生命週期。如果程序重新啟動,系統會根據狀態還原為預設限制。