Android LMK(Low Memory Killer)机制

在 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)

3. LMK 的触发流程:基于 PSI 的压力检测

在 Android 13/AAOS 中,lmkd 通过监控内核提供的 PSI 来评估系统压力。PSI 会告诉系统: "因为内存不足,任务等待 CPU 或 IO 的时间占比是多少?"

  1. 监控 (Monitoring): lmkd 监听内核的 PSI 事件。

  2. 压力分级:

    • Low Pressure: 内存稍微紧张,开始杀掉一些 oom_score_adj 非常高的 Cached 应用。
    • Medium Pressure: 压力增大,杀掉普通的后台应用。
    • Critical Pressure: 极度危险,甚至可能杀掉不直接可见的 Service 进程,以防止 UI 卡死。
  3. 选择目标 (Selection): 寻找当前 oom_score_adj 最大的进程。如果分数相同,通常杀掉内存占用更大的。

  4. 执行 (Kill): lmkd 发送 SIGKILL 信号强制结束进程。


4. 车载 (AAOS) 环境下的特殊性

在车载项目(如你提到的一汽大众项目)中,LMK 的调优比手机更敏感:

  • 倒车影像/全景 (Safety First): 涉及泊车等安全功能的进程通常会设置极低的 oom_score_adj,甚至会被设为 persistent,确保在任何情况下都不会被 LMK 干掉。
  • 多显示器 (Multi-Display): 即使 App 在副屏运行,它也属于 VisibleForeground 级别,oom_score_adj 会比普通后台应用低,受 LMK 保护。
  • 资源竞争: 车机系统往往长时间不重启,且会有大量的 B-Call、E-Call、导航等后台 Service。如果你在启动一个重型 App(如高清电影平台)前手动清理,确实能有效延迟 PSI 达到 Critical 阈值的时间,从而避免系统因瞬间内存剧增导致其他关键组件闪退。

5. 解决方案:

  • 在应用层方面

如果拥有系统签名文件,可以添加配置 sharedUserIdpersistent

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

    perl 复制代码
    adb logcat | grep lmkd
    # 你会看到类似 "Kill 'com.android.app' (PID), adj 900, to free 150MB" 的日志

建议: 可以结合 ActivityManager.getMyMemoryState() 或监听 onTrimMemory() 回调。如果系统已经回调了 TRIM_MEMORY_RUNNING_CRITICAL,说明 LMK 已经在"磨刀"了,此时主动释放资源或清理后台是最佳时机。

相关推荐
Libraeking30 分钟前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位1 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen1233 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs3 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob3 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔4 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9964 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly6 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首6 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-19439 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed