4. LMKD核心概念

在Android内存管理体系中,LMKD(Low Memory Killer Daemon)是运行在用户空间的一个常驻守护进程,负责在系统内存紧张时决定“杀谁、何时杀、杀多少”。它并不是一个简单的替代品,而是Android从“内核被动回收”走向“用户空间主动决策”的关键组件。理解LMKD的定位、演进历史、与内核LowMemoryKiller(LMK)的本质区别,以及它的用户空间决策机制,是后续进行LMKD调优的前提。

一、LMKD的定位:用户空间的“内存压力仲裁者”

LMKD位于Android框架层与Linux内核之间,扮演内存压力仲裁者的角色。它通过内核提供的/proc/pressure/memory(PSI,Pressure Stall Information)或传统的vmpressure事件感知内存压力,再结合进程的oom_score_adj(OOM调整值)和进程状态,选择最合适的进程终止,以释放内存并维持系统流畅。

它的核心职责可以概括为三点:

  • 感知压力:监听内核上报的内存压力事件,判断当前是轻度、中度还是重度紧张。
  • 选择目标:根据进程的优先级、前台/后台状态、占用内存大小,计算“性价比”最高的终止对象。
  • 执行终止:向目标进程发送SIGKILL,并记录日志供调试与统计。
LMKD不是“内存回收器”,它不负责页回收、不负责压缩内存,而是通过“杀进程”这一极端手段,快速释放整块内存,避免系统进入不可用的卡死状态。

二、历史演进:从内核LMK到用户空间LMKD

Android早期版本(Android 1.5至6.x)依赖内核中的LowMemoryKiller驱动。该驱动以内核模块形式存在,通过扫描进程的oom_score_adj和内存阈值,在内存低于预设水位时直接杀进程。它的优点是响应快、无需用户空间参与;缺点是策略僵化、阈值固定、无法感知前台体验,且与内核OOM Killer存在职责重叠。

从Android 7.0(Nougat)开始,Google逐步将杀进程逻辑迁移到用户空间,引入LMKD。Android 9(Pie)之后,LMKD成为默认且唯一的低内存杀进程机制,内核LMK被彻底移除。Android 10及以后,LMKD进一步引入PSI(Pressure Stall Information)作为主要压力信号,使决策更精细、更贴近真实用户体验。

对比维度 内核LowMemoryKiller(LMK) 用户空间LMKD
运行位置 Linux内核空间 Android用户空间
压力信号 空闲内存水位(free memory) PSI / vmpressure / 内存水位
策略灵活性 低,阈值固定在内核配置 高,可通过属性、配置动态调整
与框架协同 弱,无法感知Activity状态 强,结合oom_score_adj与进程状态
可观测性 内核日志,调试困难 logcat、dumpsys、统计信息丰富
Android版本 Android 6及以前 Android 7引入,Android 9后默认

三、与内核LowMemoryKiller的本质区别

两者最根本的区别在于决策依据与决策位置。内核LMK只看“还剩多少空闲内存”,当空闲内存低于某个阈值时,按oom_score_adj从大到小杀进程。这种机制的问题在于:空闲内存低并不一定代表用户正在经历卡顿,可能只是缓存占用多;而空闲内存高也不一定代表体验流畅,可能后台进程正在疯狂抢占CPU。

LMKD则引入了“压力”概念。PSI能够量化“因内存不足导致任务停顿的时间比例”,例如some avg10=0.50表示过去10秒内有0.5%的时间至少有一个任务因内存而停顿。LMKD根据压力等级(如low、medium、high、critical)采取不同策略:轻度压力时优先杀缓存进程,重度压力时才杀前台相关进程。这种“按压力分级、按优先级选择”的机制,比内核LMK的“一刀切”更符合Android多任务体验需求。

内核LMK是“内存低于水位就杀”,LMKD是“内存压力导致卡顿才杀”。前者关注内存数量,后者关注用户体验。

四、用户空间决策机制:LMKD如何选择目标

LMKD的决策流程可以概括为“感知—分级—选择—执行”四个步骤。下图展示了其核心决策链路与关键输入输出:

LMKD 用户空间决策链路 ① 感知压力 PSI / vmpressure ② 压力分级 low / medium / high ③ 选择目标 oom_score_adj + 内存 ④ 执行终止 SIGKILL + 日志 内核输入 /proc/pressure/memory 框架输入 oom_score_adj / 进程状态 配置输入 ro.lmk.* 属性 输出 logcat / dumpsys / 统计 关键属性:ro.lmk.use_psi、ro.lmk.psi_partial_stall_ms、ro.lmk.psi_complete_stall_ms、ro.lmk.swap_free_low_percentage 决策原则:优先终止 oom_score_adj 高、占用内存大、且非前台关键路径的进程

具体来说,LMKD在用户空间的决策机制包含以下关键点:

  1. 压力信号采集:优先使用PSI。LMKD读取/proc/pressure/memory,获取some和full两种停顿指标。当some avg10超过psi_partial_stall_ms对应阈值时,进入相应压力等级。
  2. 压力等级映射:LMKD将压力分为low、medium、high、critical四级。等级越高,允许终止的进程oom_score_adj范围越宽,甚至包括前台进程。
  3. 目标选择算法:在候选进程中,LMKD综合计算“释放内存收益”与“用户体验损失”。通常优先选择oom_score_adj较大(优先级低)、内存占用较大、且最近未被使用的进程。
  4. 终止与反馈:发送SIGKILL后,LMKD记录被杀进程的pid、oom_score_adj、释放内存量及当时压力等级,写入logcat和统计节点,供后续调优分析。

五、常见误区与复盘

误区一:认为LMKD会“智能学习”用户习惯。实际上LMKD的决策基于实时压力与静态优先级,并不具备长期学习能力。它的“智能”体现在压力分级和oom_score_adj的动态调整上,而非机器学习。
误区二:把LMKD当成内存优化工具。LMKD是“最后手段”,频繁触发说明系统内存确实不足。调优方向应是减少后台内存占用、优化进程优先级,而不是单纯调整LMKD阈值让杀进程更频繁。

复盘一个典型场景:某应用在后台被频繁杀死,日志显示lmkd: Kill 'com.example.app' (pid 12345), oom_score_adj=900, reason=low memory。这说明该进程的oom_score_adj较高(后台缓存进程),且系统处于内存压力状态。若希望它存活更久,应通过ActivityManager提升其优先级,或减少其内存占用,而不是直接修改LMKD的杀进程阈值。

# 查看LMKD相关属性
adb shell getprop | grep lmk

# 查看内存压力
adb shell cat /proc/pressure/memory

# 查看LMKD日志
adb logcat -s lmkd

# 查看进程oom_score_adj
adb shell cat /proc/<pid>/oom_score_adj

总结来说,LMKD是Android内存管理从内核态走向用户态、从“数量驱动”走向“压力驱动”的产物。它通过PSI感知真实卡顿压力,结合进程优先级进行分级决策,在系统内存紧张时以最小体验代价释放内存。理解其定位、演进、与内核LMK的区别以及用户空间决策机制,是后续进行LMKD参数调优和内存问题排查的基础。