在 Android 系统中,LMK (Low Memory Killer) 是内存管理的"最后一道防线"。它的核心任务是在系统内存不足时,按照优先级倒序杀掉进程,以保证系统核心服务的运行和用户当前正在使用的应用(Foreground App)的流畅性。
作为 Android 13/AAOS 开发者,理解 LMK 的工作原理能帮你更好地优化车载应用的生命周期管理。
LMK 日志 lowmemorykiller:
yaml
M01EECC 12-25 14:53:54.854 326 326 I lowmemorykiller: Kill 'com.xxx.xxxx' (1538), uid 1000, oom_score_adj 107 to free 111348kB rss, 60552kB swap; reason: <swap is extremely low (124936kB < 125152kB)>
1. 核心机制:oom_score_adj 评分系统
Android 为每个进程分配了一个重要性评分,称为 oom_score_adj(取值范围 -1000 到 1000)。分值越高,进程越容易被 LMK 杀掉。
系统根据应用当前的状态动态更新这个分值:
| 进程状态 | oom_score_adj (近似值) | 描述 |
|---|---|---|
| Native/System | -1000 | 绝对不能杀掉的核心进程(如 init, system_server)。 |
| Persistent | -700 ~ -800 | 声明了 persistent 的系统应用。 |
| Foreground | 0 | 当前用户正在交互的应用进程。 |
| Visible | 100 ~ 200 | 用户可见但不在前台(如分屏应用、被透明 Activity 覆盖的应用)。 |
| Perceptible | 200 | 用户能感知到的应用(如正在播放音乐、有前台 Service)。 |
| Cached / Background | 900 ~ 1000 | 已经退到后台且没有任务的应用。最先被清理。 |
2. 进化:从内核到用户态 (lmkd)
-
传统方式 (Kernel LMK): 早期 Android 使用 Linux 内核驱动程序。它根据系统剩余内存(Free Memory)是否达到预设的"水位线"(Minfree)来触发杀进程逻辑。
-
现代方式 (Userspace
lmkd): 从 Android 9.0 开始,核心逻辑移到了用户空间守护进程lmkd。- Android 13 默认使用
lmkd。 - 它不再只看"剩余内存",而是看 PSI (Pressure Stall Information) 。
- Android 13 默认使用
3. LMK 的触发流程:基于 PSI 的压力检测
在 Android 13/AAOS 中,lmkd 通过监控内核提供的 PSI 来评估系统压力。PSI 会告诉系统: "因为内存不足,任务等待 CPU 或 IO 的时间占比是多少?"
-
监控 (Monitoring):
lmkd监听内核的 PSI 事件。 -
压力分级:
- Low Pressure: 内存稍微紧张,开始杀掉一些
oom_score_adj非常高的 Cached 应用。 - Medium Pressure: 压力增大,杀掉普通的后台应用。
- Critical Pressure: 极度危险,甚至可能杀掉不直接可见的 Service 进程,以防止 UI 卡死。
- Low Pressure: 内存稍微紧张,开始杀掉一些
-
选择目标 (Selection): 寻找当前
oom_score_adj最大的进程。如果分数相同,通常杀掉内存占用更大的。 -
执行 (Kill):
lmkd发送SIGKILL信号强制结束进程。
4. 车载 (AAOS) 环境下的特殊性
在车载项目(如你提到的一汽大众项目)中,LMK 的调优比手机更敏感:
- 倒车影像/全景 (Safety First): 涉及泊车等安全功能的进程通常会设置极低的
oom_score_adj,甚至会被设为persistent,确保在任何情况下都不会被 LMK 干掉。 - 多显示器 (Multi-Display): 即使 App 在副屏运行,它也属于 Visible 或 Foreground 级别,
oom_score_adj会比普通后台应用低,受 LMK 保护。 - 资源竞争: 车机系统往往长时间不重启,且会有大量的 B-Call、E-Call、导航等后台 Service。如果你在启动一个重型 App(如高清电影平台)前手动清理,确实能有效延迟 PSI 达到 Critical 阈值的时间,从而避免系统因瞬间内存剧增导致其他关键组件闪退。
5. 解决方案:
- 在应用层方面
如果拥有系统签名文件,可以添加配置 sharedUserId 和 persistent。
ini
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:sharedUserId="android.uid.system">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:persistent="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:directBootAware="true"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Launcher">
</application>
</manifest>
- 在 Framework 方面
adb shell 查看是否配置白名单:
bash
/vendor/etc/lmkd_param.conf
在这个目录 Framework 目录下添加要保护的包名,配置白名单
bash
./device/sprd/mpool/module/vendor/memory/lmkd/msoc/qogirn6pro/lmkd_param.conf
6. 调试工具
如果你想观察实时的 LMK 行为,可以使用以下命令:
-
查看当前各进程得分:
Bash
adb shell ps -Ao pid,args,oomscore -
查看
lmkd日志:Bash
perladb logcat | grep lmkd # 你会看到类似 "Kill 'com.android.app' (PID), adj 900, to free 150MB" 的日志
建议: 可以结合
ActivityManager.getMyMemoryState()或监听onTrimMemory()回调。如果系统已经回调了TRIM_MEMORY_RUNNING_CRITICAL,说明 LMK 已经在"磨刀"了,此时主动释放资源或清理后台是最佳时机。