程序記憶體守護精靈

Android 17 以上版本支援程序記憶體守護程式 (PMGD),可主動管理各程序的記憶體用量,保護系統健康狀態和使用者體驗。這個 Daemon 會對特定目標程序強制執行記憶體上限,並驗證隔離的記憶體流失或高點不會導致系統效能全面下降,進而提升裝置整體穩定性。

傳統的全域低記憶體終止程序只會在整個系統承受壓力時運作,但 PMGD 採取的是精細方法。精靈會監控目標程序的 Control Group v2 記憶體值,藉此達成上述目的。當目標程序超出設定的記憶體限制時,pmgd 會記錄 Statsd 記憶體原子,然後終止程序,藉此處理違規行為。

運作方式

精靈會使用 inotify 監聽記憶體壓力事件 (特別是使用 memory.events 的高記憶體活動)。當受監控的程序觸發記憶體事件時,pmgd 會執行下列動作:

  1. 匿名記憶體檢查:評估程序的匿名記憶體。如果超過設定的 anon_limit_in_mbpmgd 會立即終止程序。
  2. 回收等待期:如果匿名記憶體低於指定的匿名記憶體限制,pmgd 會等待系統回收寬限期 (reclaim_wait_time_secs)。
  3. 回收後的記憶體評估:如果目標程序的 memory.current 在寬限期過後仍大於或等於 memory.high匿名記憶體超過 anon_limit_in_mbpmgd 會立即終止程序。

這個程序會持續執行,直到程序遭終止,或程序回收記憶體,使記憶體用量低於指定限制為止。

系統健康狀態功能

  • 重新啟動頻率限制:為避免循環啟動或持續當機,pmgd 會追蹤 /data/misc/pmgd/history.json 中的程序終止情形。在每次重新啟動裝置時,這個精靈會將程序限制為單一 pmgd 啟動的終止程序。

SELinux 設定

SELinux 政策會限制 PMGD 監控程序的權限。如果您設定 PMGD 監控政策不允許的程序網域 (例如廠商專屬的系統程序),PMGD 就無法監控該程序,且您可能會在 logcat 中看到 SELinux 拒絕訊息。

如要允許 PMGD 監控其他網域中的程序,您必須更新 PMGD 的裝置專屬 SELinux 政策,藉此擴充 PMGD 的權限。

以下是 device/<vendor>/<device>/sepolicy/pmgd.te 檔案範例,可新增對新網域的存取權:

# Allow pmgd to access vendor_system_apps
r_dir_file(pmgd, vendor_system_apps)

如要進一步瞭解如何編寫裝置專屬政策,請參閱「實作 SELinux」。

供應商定義的設定

PMGD 設定是由供應商主導,並透過必要的 JSON 檔案 /vendor/etc/pmgd/config.json 進行設定。這份清單會列出要追蹤的程序、設定的記憶體限制設定檔 (使用 cgroup 工作設定檔),以及以 MB 為單位的匿名記憶體硬性限制。

供應商設定欄位

提供的 JSON 設定是程序及其限制的清單,由下列欄位定義:

欄位 類型 必填 說明 預設
target_cmd 字串 要監控的目標程序指令名稱,例如 system_server 不適用
uid 整數 程序的使用者 ID (UID)。如果省略,pmgd 會將規則全域套用至任何符合 target_cmd 的程序。 不適用
reclaim_wait_time_secs 整數 系統在重新評估記憶體限制前,等待回收記憶體的緩衝時間 (以秒為單位)。 5
mem_limit_profile 字串 設定 `memory.high` 的 cgroup 工作設定檔名稱。這項設定用於設定程序記憶體限制。 不適用
anon_limit_in_mb 整數 以 MB 為單位的最終匿名記憶體限制。如果匿名記憶體使用率超過這個值,pmgd 會立即終止程序。 不適用
additional_task_profiles 字串清單 監控作業開始時,適用於程序的任何其他工作設定檔清單。pmgd 空白清單

以下是 vendor/etc/task_profiles.json 中 cgroup 工作設定檔的設定範例:

{
  "Attributes": [
    ...
    {
      "Name": "MemHigh",
      "Controller": "memory",
      "File": "memory.high"
    }
  ],
  "Profiles": [
    {
      "Name": "SystemServerMemoryHighLimit",
      "Actions": [
        {
          "Name": "SetAttribute",
          "Params":
          {
            "Name": "MemHigh",
            "Value": "1080M"
          }
        }
      ]
    }
  ]
}

以下是 vendor/etc/pmgd/config.json中 PMGD 設定的範例:

{
  "targets": [
    {
      "target_cmd": "system_server",
      "uid": 1000,
      "reclaim_wait_time_secs": 5,
      "mem_limit_profile": "SystemServerMemoryHighLimit",
      "anon_limit_in_mb": 300
    }
  ]
}