記憶體管理 Daemon

Android 17 以上版本支援記憶體管理 Daemon (mmd),這是一種系統 Daemon,可處理 Daemon 設定、可調整項目,以及持續進行的交換或 ZRAM 維護工作。

背景

mmd 推出前,Android 的 ZRAM 設定相當分散,且自訂程度有限。mmd 集中管理 ZRAM,啟用更精密的設定邏輯,並簡化新功能和架構改良項目的新增作業,解決上述問題。mmd,也能明確區分以 Java 為基礎的system_server程序,以及核心層級的交換或記憶體管理。

架構和 ZRAM 管理

開機完成後 (即 sys.boot_completed=1),mmd_setup 會嘗試使用指定參數設定 ZRAM。ZRAM 設定完成後,系統會啟用 mmd 服務,處理持續進行的維護工作。

透過 mmd 專案,維護作業會從 system_server 啟動,方法是使用 IMmd 介面將繫結器要求傳送至 mmdmmd 會根據本身的內部政策引擎,執行 ZRAM 寫回、重新壓縮和每個程序的寫回等維護工作。您可以使用系統屬性,設定 ActivityManagerService 的排程和 ZRAM 維護政策。

系統伺服器整合 (system_server)

Java 架構的 system_server 程序會決定何時叫用 mmd。這項程序會將全域維護掃描作業與針對個別應用程式的記憶體最佳化作業分開。

正常的後續處理維護

ActivityManagerService 會使用 com.android.server.memory.ZramMaintenance 執行全域 ZRAM 維護作業。

zram-maintenance

圖 1. ZRAM 維護作業排程流程。

  • 排程引擎: ZramMaintenance 使用 Android 的 JobScheduler 註冊週期性背景工作。
  • 工作限制:為避免前景 UI 發生延遲或 CPU 爭用情形,工作會明確設定 setRequiresDeviceIdle(true)setRequiresBatteryNotLow(true)
  • 繫結器觸發:排程器觸發 onStartJob() 時,system_server 會叫用 mmd.doZramMaintenanceAsync()。這是單向非同步的 Binder 呼叫;system_server 不會封鎖,等待維護掃描完成。mmd 會將此作業排入背景工作執行緒,依序執行重新壓縮和寫回作業。

每個程序的回寫

ActivityManagerService 會使用 com.android.server.am.CachedAppOptimizer 管理每個程序的記憶體清除作業。

mmd-writeback

圖 2. 每個程序的 mmd 回寫流程。

當程序轉換為背景快取狀態時,ActivityManager 會執行記憶體壓縮。如果使用者會看到程序因記憶體不足而終止,也就是程序會代管 Activity,且 ZRAM 每個程序的寫回作業會將程序的記憶體用量降至接近零,系統就會採取下列步驟:

  1. 壓縮完成後,CachedAppOptimizer 會將延遲訊息 (ZRAM_WRITEBACK_MSG) 傳送至內部壓縮處理常式 (延遲時間為 mZramWritebackWaitSeconds)。
  2. 延遲時間到期後,ActivityManager 會開啟安全程序檔案描述元 pidfd
  3. 系統伺服器會呼叫 mmd.asyncWritebackProcessZramMemory(pfd, callback)
  4. mmd 會執行每個程序的寫回 ioctl,並使用 IMmdProcessWritebackCallback 回報。如果成功,ActivityManager 會標記程序記錄 (setIsZramWrittenBack(app, true)),以提升程序的 oom_score_adj,並將指標記錄到 FrameworkStatsLog.ZRAM_WRITEBACK_EVENT

每個程序的預先擷取

當使用者重新啟動先前已快取的應用程式 (因 UNFREEZE_REASON_ACTIVITY 而解除凍結) 時,ActivityManager 會將主要頁面錯誤從備份儲存空間造成的應用程式啟動延遲降至最低:

  1. CachedAppOptimizer 會攔截取消凍結事件,並叫用 prefetchZram(app)
  2. 系統伺服器會使用 mmd.asyncPrefetchProcessZramMemory(pfd),透過 Binder 傳送應用程式的 pidfdmmd 會發出 ZRAM_ANDROID_IOC_PROCESS_PREFETCH ioctl,指示核心在應用程式的主要 UI 執行緒初始化時,非同步預先擷取換出的頁面並放回 RAM。

維護和後續處理工作總覽

本節說明 mmd 執行的背景維護作業和後續處理工作,以最佳化交換空間和系統記憶體。

mmd 中的維護

mmd 中,「維護」是指排定的背景維護掃描,可最佳化交換空間和實體記憶體使用率,且不會影響使用中使用者前景效能。系統不會執行連續的同步掃描 (這會導致 CPU 大量喚醒和 UI 延遲),而是以非同步方式執行維護作業:

  1. system_server 會定期在 Binder 中觸發 doZramMaintenanceAsync()

  2. mmd 會將要求放在背景工作佇列 LowPrioWorkItem::ZramMaintenance 中。

  3. mmd 中有一個工作執行緒,可同時管理高優先順序佇列和低優先順序佇列。系統會優先處理高優先順序工作項目 (例如每個程序的預先擷取),並可先佔低優先順序工作項目。維護和每個程序的寫回作業會以低優先順序工作項目執行。彈出時,背景工作執行緒會依序執行兩項主要維護作業:

    • ZRAM 重新壓縮:掃描現有的交換頁面,並使用較高比率的次要壓縮演算法 (例如 zstd) 重新壓縮閒置頁面。

    • ZRAM 寫回:掃描閒置頁面,並將這些頁面從 RAM 完全逐出,然後透過 /data 上的檔案,將這些頁面寫回備份快閃儲存空間的迴路裝置。

ZRAM 中的後續處理工作

在 Linux 核心 ZRAM 模組和 mmd 架構中,後續處理工作是指套用至記憶體頁面的非同步轉換,前提是這些頁面已由核心的標準回收路徑 (kswapd 或壓縮) 換出。

系統最初交換出網頁時,會優先考量速度:使用快速的主要壓縮演算法 (例如 lz4),並將壓縮的網頁儲存在 RAM 中。不過,隨著時間推移,許多交換的頁面會變成冷頁面或閒置頁面,例如數小時未恢復的背景快取應用程式。將冷頁面留在快速、輕度壓縮的 ZRAM 中效率不彰。

後續處理管道

mmd 實作多級式後續處理生命週期,以最佳化這些頁面:

mmd-page-lifecycle

圖 3. mmd 網頁生命週期。

  1. 階段 1:初始換出 (快速壓縮):系統會先透過 kswapd 或應用程式壓縮回收記憶體。通常第一次回收作業會使用 lz4 等快速壓縮演算法,並將內容儲存在 RAM 中。

  2. 階段 2:閒置標記 (老化和追蹤): mmd 閒置追蹤存取權 核心記憶體追蹤 (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) 或使用軟體閒置標記,追蹤網頁閒置時間。

  3. 階段 3:後續處理 1 - 重新壓縮 (記憶體回收): 達到重新壓縮閒置時間 (min_idle_secondsmax_idle_seconds) 的網頁會重新壓縮。mmd 會寫入 /sys/block/zram0/recompress,指示核心解壓縮 lz4 頁面,然後使用 zstd 重新壓縮。這項作業會回收實體 RAM, 不會造成快閃寫入耗損。

  4. 階段 4:後續處理 2 - 寫回 (清除至快閃儲存空間):如果記憶體壓力持續存在,且網頁達到寫回閒置時間 (通常為 20 小時以上),mmd 會觸發寫回作業。mmd 會寫入 /sys/block/zram0/idle/sys/block/zram0/writeback,將壓縮頁面從 RAM 完全逐出至備份快閃儲存空間。

ZRAM 設定配置

mmd 會載入及處理下列 ZRAM 設定屬性:

屬性 使用 預設
mmd.zram.enabled 是否啟用 mmd ZRAM 設定。 false
mmd.zram.num_devices 要設定的 ZRAM 裝置數量。如要使用數字 N,系統必須先偵測到裝置 zram0zram<N-1>,才能設定 sys.boot_completed=1。您可以為每個裝置設定 ZRAM 裝置清單中的屬性。 1
mmd.zram.device_priority 呼叫 swapon 時要傳遞的優先順序值。 未設定
mmd.zram.comp_algorithm ZRAM 壓縮演算法。如未指定,系統會使用核心預設壓縮演算法。 未設定
mmd.zram.size 以位元組為單位的 ZRAM 裝置大小,或裝置 RAM 大小的百分比,例如: 75% 50%
mmd.zram.writeback.enabled 是否啟用 ZRAM 寫回功能。 false
mmd.zram.writeback.device_size 回寫裝置的大小 (以位元組為單位),或資料分割區的百分比。實際裝置大小可根據資料分割區的可用空間調整。 1073741824 (1 GiB)
mmd.zram.writeback.min_free_space_mib 寫回裝置設定後,必須提供的最小可用空間 (以 MiB 為單位)。 1536 (1.5 GiB)
mmd.zram.writeback.use_nr_tags_prop true 使用 mmd.zram.writeback.nr_tags 中的值時,會設定迴路裝置的佇列深度,以支援 ZRAM 寫回。如果無法設定供應商 SELinux 政策,允許 mmd 直接讀取區塊裝置備份 nr_tagsnr_tags,可以採用這個解決方法。/data false
mmd.zram.writeback.nr_tags 詳情請參閱《mmd.zram.writeback.use_nr_tags_prop》。 未設定
mmd.zram.recompression.enabled 是否啟用 ZRAM 重新壓縮功能。 false
mmd.zram.recompression.algorithm 次要 ZRAM 重新壓縮演算法。 zstd

每個 ZRAM 裝置的屬性

如果 mmd.zram.num_devices 大於 1,您可以將屬性設為以半形逗號分隔的值 (內含的元素數量必須正好是 mmd.zram.num_devices 個),選擇性地為每個 ZRAM 裝置設定特定屬性。這些屬性包括:

  • mmd.zram.size
  • mmd.zram.comp_algorithm
  • mmd.zram.device_priority
  • mmd.zram.recompression.enabled
  • mmd.zram.recompression.huge_idle.enabled
  • mmd.zram.recompression.idle.enabled
  • mmd.zram.recompression.huge.enabled
  • mmd.zram.recompression.threshold_bytes
  • mmd.zram.recompression.algorithm
  • mmd.zram.writeback.device_size
  • mmd.zram.writeback.huge_idle.enabled
  • mmd.zram.writeback.idle.enabled
  • mmd.zram.writeback.huge.enabled

現有 ZRAM 設定即將淘汰

雖然 Android 仍提供 swapon_all,可設定 ZRAM 和以磁碟為基礎的交換空間,但 mmd 是 ZRAM 管理的偏好方法,可簡化設定並提供 ZRAM 重新壓縮等進階功能。

mmd啟用 mmd.zram.enabled ZRAM 設定後:

  • swapon_all 實作中的 ZRAM 設定會變成無運算。
  • 系統會忽略現有的 ZRAM 設定,例如疊加層 config_zramWriteback 檔案中的 config.xmlro.zram.* 回寫系統屬性。

ZRAM 維護可調整項目

ZRAM 維護作業應該可以立即運作,您可以使用本節中的系統屬性進一步微調。

ZRAM 維護排程

這些屬性會控管 system_server 排定 ZRAM 維護工作的時間和方式。

屬性 使用 預設
mm.zram.maintenance.first_delay_seconds 啟動第一次 ZRAM 維護作業前的延遲時間。 3600 (1 小時)
mm.zram.maintenance.periodic_delay_seconds 後續 ZRAM 維護排程之間的延遲時間。 3600 (1 小時)
mm.zram.maintenance.require_device_idle 是否只在裝置閒置時啟動 ZRAM 維護作業。 true
mm.zram.maintenance.require_battery_not_low 是否要在啟動 ZRAM 維護作業前,要求電池電量充足。 true

ZRAM 回寫政策

下列參數可控制寫入備份裝置的記憶體類型和時間:

屬性 使用 預設
mmd.zram.writeback.backoff_seconds 上次回寫作業後經過的延遲時間。 600 (10 分鐘)
mmd.zram.writeback.min_idle_seconds mmd.zram.writeback.max_idle_seconds 搭配使用,根據記憶體使用率分數計算頁面的閒置時間,判斷是否符合回寫資格。系統會在這兩個參數之間以指數方式插補計算出的閒置時間,盡量減少工作量,同時避免記憶體壓力。 72000 (20 小時)
mmd.zram.writeback.max_idle_seconds 系統會根據記憶體用量動態計算閒置網頁的存留時間,這個屬性就是計算時使用的秒數上限。 90000 (25 小時)
mmd.zram.writeback.huge.enabled 是否啟用 HUGE 頁面回寫。 false
mmd.zram.writeback.idle.enabled 是否啟用 IDLE 頁面回寫。 true
mmd.zram.writeback.huge_idle.enabled 是否啟用 HUGE_IDLE 頁面回寫。 true
mmd.zram.writeback.min_bytes 一次閒置寫回作業的最低寫回位元組數。 5242880 (5 MiB)
mmd.zram.writeback.max_bytes 一次閒置寫回作業中寫回的位元組數上限。 314572800 (300 MiB)
mmd.zram.writeback.max_bytes_per_day 24 小時內寫回的位元組數上限。 25769803776 (24 GiB)
mmd.zram.writeback.limit.enabled 是否啟用每日回寫預算上限會計。 true

ZRAM 重新壓縮政策

下列參數可控制記憶體重新壓縮的時間和類型:

屬性 使用 預設
mmd.zram.recompression.backoff_seconds 上次重新壓縮後的回溯時間。 1800 (30 分鐘)
mmd.zram.recompression.min_idle_seconds mmd.zram.recompression.max_idle_seconds 搭配使用,計算網頁的閒置時間,判斷網頁是否符合根據記憶體使用率分數重新壓縮的資格。系統會在這兩個參數之間以指數方式插補計算出的閒置時間,盡量減少工作量,同時避免記憶體壓力過大。 7200 (2 小時)
mmd.zram.recompression.max_idle_seconds 計算閒置網頁時間上限 (以秒為單位)。 14400 (4 小時)
mmd.zram.recompression.threshold_bytes 系統會考慮重新壓縮的 ZRAM 頁面大小下限 (以位元組為單位)。 1024 (1 KiB)
mmd.zram.recompression.huge.enabled 是否啟用HUGE頁面重新壓縮。 true
mmd.zram.recompression.idle.enabled 是否啟用IDLE頁面重新壓縮。 true
mmd.zram.recompression.huge_idle.enabled 是否啟用HUGE_IDLE頁面重新壓縮。 true

ZRAM 閒置頁面追蹤

mmd ZRAM 維護作業會根據 ZRAM 頁面上次存取時間,將這些頁面標示為閒置。這項功能需要啟用 CONFIG_ZRAM_TRACK_ENTRY_ACTIMECONFIG_ZRAM_MEMORY_TRACKING 核心設定。在 GKI 核心 6.18 以上版本中,CONFIG_ZRAM_TRACK_ENTRY_ACTIME 預設為啟用。在較舊的 Kernel 中,這項功能會造成記憶體負擔,因此預設為停用。

如果未啟用核心設定,mmd ZRAM 維護作業會改用軟體替代邏輯,追蹤閒置的 ZRAM 頁面:

  1. mmd 啟動時,將所有 ZRAM 頁面標示為閒置。

  2. 略過下一次 ZRAM 維護作業,直到必要的回退期結束為止。

  3. ZRAM 寫回或重新壓縮閒置頁面。如果因回寫限制而有閒置頁面,mmd 會在下一次維護時繼續回寫頁面,不會將新頁面標示為閒置 (略過步驟 4)。

  4. 如果所有閒置頁面都已寫回,請再次將所有 ZRAM 頁面標示為閒置,然後返回步驟 2。如果停用 ZRAM 寫回功能,mmd 會在重新壓縮閒置時間過後,重新壓縮 ZRAM 時,將所有 ZRAM 頁面標示為閒置。

疑難排解與驗證指南

請按照下列驗證步驟和疑難排解程序,驗證及診斷 mmd 和 ZRAM 作業。

驗證 ZRAM 設定

如要確認 mmd 在開機期間是否已成功設定 ZRAM,請執行下列步驟:

  1. 檢查有效的壓縮演算法和磁碟大小:

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. 驗證mmd系統屬性和執行中的服務狀態:

    getprop | grep mmd.zram
    dumpsys -l | grep mmd
    

驗證 ZRAM 維護和回寫

確認 ZRAM 寫回和重新壓縮維護工作是否正常運作:

  1. 檢查支援的區塊裝置狀態:

    cat /sys/block/zram0/bd_stat
    
  2. 監控 /sys/block/zram0/mm_stat,瞭解重新壓縮效率。 維護週期結束後,壓縮資料大小的變化就會顯示出來。

驗證每個程序的回寫

您可以使用下列項目,驗證每個程序的回寫功能是否正常運作:

  • 檢查 adb logcat -s mmd,確認是否成功寫回記錄或失敗診斷。

常見問題和診斷

使用者可能會遇到下列常見錯誤情況:

  • WritebackDailyLimitExceeded這個錯誤表示已達 mmd.zram.writeback.max_bytes_per_day 配額上限。發生這種情況時,mmd 會暫停閒置寫回作業,直到 24 小時的滾動時間範圍前進為止。
  • Process prefetch or writeback failedioctl 失敗時,可以在 logcat 中觀察到這項錯誤。常見原因包括:
    • EBADFESRCH:目標程序在 mmdpidfd 分派至核心之前結束。
    • ENOSPC:備份儲存空間分割區已滿,或迴路裝置佇列已用盡。
  • ZRAM 未設定:如果 mmd 無法在開機時設定 ZRAM,可能是因為舊版 swapon_all 或供應商的 init 指令碼在 mmd 執行前鎖定 /dev/block/zram0