2、LMK(Low Memory Killer)原理

好,咱们进入正题。LMK 这个东西,说白了就是 Android 系统里的「内存判官」。系统内存不够用的时候,谁该留下,谁该被干掉,全由它说了算。

我记得刚入行那会儿,遇到一个挺诡异的问题——手机用着用着突然卡死,然后黑屏重启。查了半天,发现是 LMK 把系统服务给杀了。嗯,那会儿我才真正意识到,搞懂 LMK 有多重要。

2.1 LMK 的工作流程

LMK 的工作流程其实不复杂。我把它拆成三步,你一看就明白:

  1. 监控内存水位:LMK 会一直盯着系统的可用内存。它通过内核的 kswapd 线程获取内存压力信息。
  2. 触发杀进程:当可用内存低于某个阈值,LMK 就开始干活了。它会遍历所有进程,挑出「最该杀」的那个。
  3. 释放内存:杀掉进程后,系统回收内存。如果还不够,继续杀下一个。

你想想看,这个过程是不是有点像「挤牙膏」?内存不够了,就杀一个进程,释放一点空间。还不够?再杀一个。直到系统能正常运行为止。

核心要点:LMK 不是等到内存彻底用光了才动手。它会在内存还剩下一些的时候就开始杀进程。这叫「提前干预」,避免系统彻底卡死。

我在项目中遇到过一个问题:某款低端机型,杀进程杀得太狠了。用户切回微信,发现已经被杀了,得重新加载。体验很差。后来怎么解决的?调整了阈值,让 LMK 别那么「积极」。

2.2 oom_adj_score 详解

LMK 怎么决定先杀谁?靠的就是 oom_adj_score。这个值,我习惯叫它「优先级分数」。

分数越高,越容易被杀。分数越低,越安全。就这么简单。

oom_adj_score 进程类型 说明
-17 系统核心进程 永远不会被杀。比如 init、zygote
0 前台进程 用户正在交互的 App,优先级很高
1 可见进程 用户能看到但不在前台,比如悬浮窗
2 服务进程 后台运行的服务,比如音乐播放
3-5 后台进程 用户切到后台的 App
6-15 空进程 没有任何组件的进程,最先被杀

这里有个细节要注意:oom_adj_score 是动态调整的。比如一个 App 从前台切到后台,它的分数会从 0 变成 3-5。系统会根据进程的状态实时更新这个值。

避坑指南:我曾经调试过一个 bug,某个 App 明明在前台,却被 LMK 杀了。查了半天,发现是 App 自己调了 setOomAdj 把分数改高了。嗯,自己给自己挖坑,这种事还真不少见。

2.3 LMK 阈值文件位置

LMK 的配置都在 /sys/module/lowmemorykiller/parameters/ 目录下。我直接给你列出来:

/sys/module/lowmemorykiller/parameters/
├── adj              # 优先级阈值数组
├── minfree          # 内存阈值数组(单位:页)
├── cost             # 内存成本参数
└── debug_level      # 调试级别

这两个文件最关键:

  • adj:定义了不同等级的 oom_adj_score 阈值。比如 0, 1, 2, 3, 5, 15
  • minfree:定义了对应的内存阈值。比如 1024, 2048, 4096, 8192, 16384, 24576(单位是页,1页=4KB)。

它们是一一对应的。举个例子:

adj:      0,   1,   2,   3,   5,  15
minfree:  1024, 2048, 4096, 8192, 16384, 24576

当可用内存低于 24576 页(约 96MB)时,LMK 会杀掉所有 oom_adj_score 大于等于 15 的进程。如果还不够,继续杀大于等于 5 的,以此类推。

注意:不同厂商、不同机型,这些值可能不一样。我见过有些厂商把阈值设得很激进,结果导致频繁杀后台。也见过设得太保守,系统卡成 PPT。调这个参数,真的是门艺术。

你可以用 adb shell cat 命令查看当前设备的配置:

adb shell cat /sys/module/lowmemorykiller/parameters/adj
adb shell cat /sys/module/lowmemorykiller/parameters/minfree

我个人习惯在调试时先看看这两个值。很多时候,问题就出在这里。

好了,LMK 的原理就讲这么多。下一节咱们聊聊怎么调优这些阈值,让它既不会杀得太狠,也不会让系统卡死。你想想看,是不是挺有意思的?