本頁面說明 Android 上 kswapd 和 lmkd 之間的互動,並示範兩者如何搭配運作,管理記憶體壓力。
背景
當系統記憶體不足時,Android 會使用兩種主要機制回收記憶體:
kswapd:標準 Linux 核心 Daemon,可透過清除乾淨的檔案支援頁面,或將匿名頁面交換至 ZRAM 來回收頁面。lmkd:Android 的使用者空間記憶體不足終止工具 Daemon。當kswapd無法滿足記憶體需求,且系統壓力增加 (如 PSI 所示) 時,lmkd會介入終止程序,釋出較大的記憶體區塊。
kswapd 和 lmkd 是兩個互補的記憶體回收系統。兩者都致力於達成相同目標 (建立可用記憶體),但方法不同。比較與對照:
- 可復原與破壞性:
kswapd會捨棄可從儲存空間讀回的記憶體頁面,或將記憶體頁面交換至其他記憶體分區或儲存空間,以便視需要讀回。lmkd會終止程序,進而破壞非揮發性狀態。 - 回收頻寬:
kswapd會比lmkd耗用更多 CPU 來回收指定數量的可用記憶體,且回收該記憶體所需的時間也較長。lmkd可以在短時間內回收大量記憶體,且相對不費力。請注意,在行動裝置上,CPU 效能不僅會影響延遲時間,也會影響電池電力。
OOM 分數值區
Android 會根據程序對使用者的重要性,為程序指派記憶體不足 (OOM) 分數調整值 (oom_score_adj)。系統會優先終止分數較高的程序。
常見的 bucket 包括:
- 快取 (分數 >= 900):目前不需要的程序,保留這些程序可加快重新啟動速度。
- 服務 (分數 500):託管已啟動服務的程序。
- 前景服務 (分數 200):代管前景服務的程序。
- 前景 (分數 0):使用者目前互動的應用程式。
深入探討:PSI 和 LMKD 觸發
什麼是 PSI?
壓力停滯資訊 (PSI) 是 Linux 核心功能,可提供標準方法來測量資源短缺情況。這項指標會追蹤因 CPU、記憶體和 I/O 不足而延遲的工作時間。
記憶體 PSI:「some」與「full」
記憶體 PSI 專門追蹤記憶體不足造成的延遲 (例如等待頁面回收或重新錯誤)。這項工具提供兩項指標:
some:至少有一項工作因記憶體而停滯的時間百分比。這表示記憶體不足會影響效能,但部分工作仍在進行中。full:所有非閒置工作同時停滯的時間百分比。這表示系統完全遭到封鎖,等待記憶體釋出,處於嚴重狀態。
PSI 如何觸發 LMKD
使用者空間 lmkd 守護程序會向核心註冊,以便在超過 PSI 門檻時接收通知 (透過監控 /proc/pressure/memory)。當系統遇到高記憶體壓力時 (例如 full 停滯超過設定的時間長度),核心會喚醒 lmkd。接著,lmkd 會評估系統狀態,並根據 OOM 分數和大小,決定是否終止程序以及要終止哪些程序。
LMKD 調整
LMKD 行為可透過系統屬性調整。
在裝置上尋找微調值
如要查看連結裝置目前的 LMKD 調整值,請執行以下指令:
adb shell getprop | grep lmk
常見 LMKD 屬性
以下是用於調整 LMKD 的一些重要屬性 (完整清單請參閱 Android 說明文件):
ro.lmk.use_psi:設為true,即可使用 PSI 監控器偵測記憶體壓力 (Android 10 以上版本預設為啟用)。ro.lmk.psi_partial_stall_ms:some(部分) 停滯時間長度 (以毫秒為單位) 的閾值,達到此值就會觸發終止 (一般裝置預設為 70 毫秒,低 RAM 裝置預設為 200 毫秒)。ro.lmk.psi_complete_stall_ms:full停滯時間長度 (以毫秒為單位) 的閾值,達到此值就會觸發終止 (預設為 700 毫秒)。ro.lmk.kill_heaviest_task:如果為true,LMKD 會終止相同 OOM 值區中候選項目中記憶體用量最多的程序。
測試裝置 (Google Pixel 8 Pro) 上的相關調整值
(注意:搭載 Android 15 的高階裝置一般預設值)
ro.lmk.use_psi:truero.lmk.psi_partial_stall_ms:70(一般)ro.lmk.psi_complete_stall_ms:700(一般)ro.lmk.kill_heaviest_task:true
ZRAM 和交換驗證
ZRAM 容量會大幅影響交換空間填滿的速度,並觸發 LMKD 終止。您可以使用下列指令,檢查裝置上的 ZRAM 和交換狀態:
# Check ZRAM sizing and usage
adb shell free -m
# Check swap partition details
adb shell cat /proc/swaps
實作練習:觸發 kswapd 和 lmkd
在本練習中,您將使用 KswapdLmkdLab 範例應用程式在不同的 OOM 值區中建立程序,然後在主要程序中耗用記憶體,觸發 kswapd 和 lmkd。
應用程式設定為自動產生子程序並分配記憶體,因此很容易成為 LMKD 的目標。
1. 建立子程序
應用程式啟動時會自動產生下列子程序:
- 快取程序 (oom_score_adj >= 900)
- 服務程序 (oom_score_adj 500)
- 前景服務程序 (oom_score_adj 200)
使用 ADB 驗證程序及其 OOM 分數:
adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab
輸出內容程式碼片段範例:
Proc # 0: fg T/A/TOP LCMNFUATI t: 0 18959:com.android.kswapdlmkdlab/u0a358 (top-activity)
Proc # 1: prcp b/ /FGS -------TI t: 0 19050:com.android.kswapdlmkdlab:foreground/u0a358 (fg-service)
Proc # 2: svc b/ /SVC -------TI t: 0 19077:com.android.kswapdlmkdlab:service/u0a358 (started-services)
Proc # 3: cch b/ /CACC -------TI t: 0 19014:com.android.kswapdlmkdlab:cached/u0a358 (cch-client-act)
程序狀態縮寫對應如下:
fg:前景 (OOM 分數 0)prcp:可感知 (前景服務,OOM 分數 200)svc:已啟動服務 (OOM 分數 500)cch:快取程序 (OOM 分數 900)
2. 觸發記憶體壓力
應用程式會自動開始在主要程序中分配記憶體。為了在高 RAM 裝置上有效進行測試,應用程式也會命令子程序分配記憶體 (例如每個 500 MB),讓 LMKD 更容易鎖定這些子程序。
監控子程序終止
應用程式會使用 IBinder.linkToDeath 機制監控這些子程序是否終止:
- 主要程序會使用
bindService繫結至目標程序中的服務。 - 連線後,系統會取得代表服務的
IBinder物件。 - 它會透過在繫結器上呼叫
linkToDeath來註冊IBinder.DeathRecipient實作。 - 當目標程序終止時 (例如由 LMKD 終止),系統會呼叫
binderDied()方法,讓應用程式以非同步方式記錄終止事件。
為避免這個繫結提高目標程序的優先順序 (這會違背練習目的),應用程式會使用 Context.BIND_WAIVE_PRIORITY 旗標。
範例應用程式實際運作情形
以下是在 Google Pixel 8 Pro (總 RAM 約 12 GB) 上完成測試執行後的應用程式螢幕截圖。
Perfetto 追蹤記錄分析
如要擷取互動,請使用下列 Perfetto 設定,擷取排程、記憶體計數器、LMK 終止和壓力停滯資訊 (PSI)。
Perfetto 設定
執行 Perfetto,並啟用排程、記憶體計數器、LMK 終止和壓力停滯資訊 (PSI),擷取互動:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/kswapd_lmkd.perfetto-trace <<EOF
buffers: { size_kb: 131072 }
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "vmscan/mm_vmscan_kswapd_wake"
ftrace_events: "vmscan/mm_vmscan_kswapd_sleep"
ftrace_events: "psi/psi_event"
}
}
}
duration_ms: 180000
EOF
查看追蹤記錄
以下螢幕截圖取自追蹤記錄,顯示 :cached (最容易終止) 程序終止前不久,到 :foreground (最不容易終止) 程序終止的時間。
您可以看到 MemFree 已經相當低,且持續承受壓力。kswapd 會持續運作,並透過捨棄 mem.rss.file 頁面,以及將 mem.rss.anon 從所有 kswapdlmkdlab 程序 (主要程序和子程序) 移至 mem.swap 來減輕壓力,也可能從這張螢幕截圖中未顯示的其他程序移動。同時,kswapdlmkdlab 應用程式會繼續分配記憶體,增加壓力的速度比 kswapd 回收的速度更快。
當壓力過高時 (psi.mem 長時間處於高位),lmkd 就會喚醒並終止部分程序。您可以看到 lmkd 遵循 OOM 分數,先終止 kswapdlmkdlab:cached,然後是 :service,最後是 :foreground。
請查看事件時間軸,並思考下列問題:
kswapd和lmkd如何互補:kswapd是第一個回應記憶體壓力的機制,但當kswapd的回收頻寬無法滿足分配需求時,lmkd會提供協助。- 記憶體釋放率:觀察
kswapd耗用的 CPU 作業時間,以及透過捨棄mem.rss.file或將mem.rss.anon交換至mem.swap來回收的記憶體。相較之下,lmkd中的短暫活動會釋出大量程序私人記憶體。
查詢
在 Perfetto UI (查詢 SQL) 中執行這些查詢,從追蹤記錄中擷取資料。
1. kswapd 活動
如要找出核心交換 Daemon 的啟用和執行時間,請執行:
SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
AND state = 'Running'
LIMIT 5;
輸出示例:
| ts | dur | 州 |
|---|---|---|
| 30317633052969 | 159180 | 執行中 |
| 30317633398347 | 3999186 | 執行中 |
| 30317638448111 | 1847941 | 執行中 |
| 30317641455354 | 1031006 | 執行中 |
| 30317643272615 | 888387 | 執行中 |
2. LMKD 終止
LMKD 會將記憶體不足終止事件寫入 instant 資料表。如要查詢所有 LMKD 終止作業,請執行以下操作:
SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;
事件名稱格式為:lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>。
kill_reason:整數,表示lmkd決定終止程序的原因 (定義於system/memory/lmkd/statslog.h):0:PRESSURE_AFTER_KILL1:NOT_RESPONDING2:LOW_SWAP_AND_THRASHING(ZRAM 交換空間極低,且發生顛簸)3:LOW_MEM_AND_SWAP(系統的可用記憶體和 ZRAM 交換空間不足)4:LOW_MEM_AND_THRASHING5:DIRECT_RECL_AND_THRASHING6:LOW_MEM_AND_SWAP_UTIL7:LOW_FILECACHE_AFTER_THRASHING8:LOW_MEM9:DIRECT_RECL_STUCK
max_thrashing:終止時的頁面顛簸百分比上限。
輸出示例:
| ts | 名稱 |
|---|---|
| 30326148679967 | lmk,2308,3,900,201,0 |
| 30330439763710 | lmk,2309,3,500,201,0 |
| 30334985999837 | lmk,2310,2,200,0,307 |
圖表
如要以圖表呈現資料,您可以使用下列 gnuplot 指令碼,在單一時間序列中繪製系統可用記憶體、kswapd 活動、lmkd 終止和 PSI。指令碼會假設它是從 Android 建構頂層目錄執行。
輸出圖表範例
下列圖表是使用上述指令碼和代表測試執行的樣本資料產生。

圖表說明:
這張圖表說明壓力測試期間,不同記憶體管理元件之間的動態互動:
- 可用記憶體減少:主要應用程式配置記憶體時,系統可用記憶體 (紫色線) 會穩定減少。
- kswapd 啟用:當可用記憶體低於特定水位時,
kswapd會喚醒 (藍色脈衝),透過交換至 ZRAM 來回收記憶體。這會顯示為間歇性尖峰。 - PSI 尖峰:由於
kswapd無法跟上高配置率,執行緒開始在記憶體配置上停滯,導致 PSI (壓力停滯資訊) (綠線) 飆升。 - LMKD 終止:當 PSI 達到臨界值 (例如概念上超過
ro.lmk.psi_complete_stall_ms門檻),且可用記憶體極低時,lmkd會介入並終止低優先順序程序 (時間 30 和 45 的橘色脈衝),釋出大量記憶體。請注意,終止後,可用記憶體會立即上升,PSI 則會下降,顯示終止作業能有效減輕壓力。________________________________________________________________________________
← 記憶體回收:剔除和交換 | ↑ 向上 | 將記憶體頁面傳回 OS →