2、LMK(Low Memory Killer)原理
好,咱们进入正题。LMK 这个东西,说白了就是 Android 系统里的「内存判官」。系统内存不够用的时候,谁该留下,谁该被干掉,全由它说了算。
我记得刚入行那会儿,遇到一个挺诡异的问题——手机用着用着突然卡死,然后黑屏重启。查了半天,发现是 LMK 把系统服务给杀了。嗯,那会儿我才真正意识到,搞懂 LMK 有多重要。
2.1 LMK 的工作流程
LMK 的工作流程其实不复杂。我把它拆成三步,你一看就明白:
- 监控内存水位:LMK 会一直盯着系统的可用内存。它通过内核的
kswapd线程获取内存压力信息。 - 触发杀进程:当可用内存低于某个阈值,LMK 就开始干活了。它会遍历所有进程,挑出「最该杀」的那个。
- 释放内存:杀掉进程后,系统回收内存。如果还不够,继续杀下一个。
你想想看,这个过程是不是有点像「挤牙膏」?内存不够了,就杀一个进程,释放一点空间。还不够?再杀一个。直到系统能正常运行为止。
核心要点: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 的原理就讲这么多。下一节咱们聊聊怎么调优这些阈值,让它既不会杀得太狠,也不会让系统卡死。你想想看,是不是挺有意思的?