4、PSI(Pressure Stall Information)机制

4.1 PSI的概念

PSI,全称 Pressure Stall Information,翻译过来就是「压力停滞信息」。

这玩意儿是 Linux 4.20 引入的,后来被 Google 移植到了 Android 内核里。它的作用很简单——告诉你系统到底有多「挤」。

什么叫「挤」?你想想看,CPU 忙不过来、内存不够用、IO 卡成狗,这些都会让任务排队等待。PSI 就是量化这种等待压力的指标。

我个人习惯把 PSI 理解成「系统的血压计」。血压高了,人就不舒服;PSI 高了,系统就卡顿。

PSI 统计的是三个维度的资源:

  • CPU:任务在等 CPU 时间片
  • Memory:任务在等内存分配
  • IO:任务在等磁盘读写完成

每个维度又分两个级别:

  • some:至少有一个任务在等待
  • full:所有任务都在等待(系统几乎停滞)

举个例子。假如系统有 4 个 CPU 核心,其中 3 个在忙,1 个空闲。这时候一个任务在等 CPU,那就是 some 压力。如果 4 个核心全满,所有任务都在排队,那就是 full 压力。

嗯,这里要注意:full 压力比 some 严重得多。full 意味着系统已经「动弹不得」了。

核心理解:PSI 不是告诉你「用了多少资源」,而是告诉你「因为资源不够,任务等了多久」。这是本质区别。

4.2 PSI与LMK的关系

说到 PSI 和 LMK 的关系,我得先讲个背景。

传统的 LMK(Low Memory Killer)是怎么工作的?它看一个阈值,比如可用内存低于 200MB,就杀进程。这种方式很粗暴——它不看系统实际感受,只看一个静态数字。

PSI 的出现,让 LMK 有了「感知能力」。

我在项目中遇到过这样一个场景:某款手机可用内存还有 300MB,按理说 LMK 不会触发。但用户已经觉得卡得不行了。为什么?因为内存碎片化严重,分配大块内存时频繁触发直接回收,导致任务大量等待。

PSI 能捕捉到这种「表面充裕、实际痛苦」的状态。

所以 Android 从 10 开始,引入了基于 PSI 的 LMKD(Low Memory Killer Daemon)。它的逻辑是:

  1. 监控 PSI 内存压力指标
  2. 当压力超过阈值时,触发不同级别的回收或杀进程
  3. 压力越大,动作越激进

说白了,LMKD 把 PSI 当成了「触发器」。PSI 说「我疼了」,LMKD 就动手。

我的经验:PSI 阈值调得太低,系统会频繁杀进程,影响用户体验。调得太高,卡顿感已经很明显了才动手。这个平衡点需要根据具体产品来调,没有万能公式。

4.3 PSI监控指标解读

PSI 的数据从哪里看?

在 Android 设备上,直接读这个文件:

/proc/pressure/memory

类似的还有 /proc/pressure/cpu/proc/pressure/io

我们拿内存为例,看看实际数据长什么样:

some avg10=2.35 avg60=1.87 avg300=0.95 total=89234567
full avg10=0.89 avg60=0.65 avg300=0.32 total=34567890

我来拆解一下:

  • avg10:过去 10 秒的平均压力百分比
  • avg60:过去 60 秒的平均压力百分比
  • avg300:过去 300 秒的平均压力百分比
  • total:从系统启动到现在的累计等待微秒数

数值的含义是什么?

avg10=2.35 表示过去 10 秒内,有 2.35% 的时间至少有一个任务在等待内存。这个百分比越高,说明内存压力越大。

我曾经踩过一个坑:只看 avg10 不看 avg300。有一次 avg10 飙到 8%,我以为是内存不够了,赶紧调高杀进程阈值。结果发现是某个测试 App 在短时间内疯狂分配内存,持续了十几秒就结束了。avg300 只有 0.5%,说明长期压力并不大。

从那以后,我养成了一个习惯:看趋势,不看单点

注意:PSI 的 total 值会不断增长,不能直接用来判断当前压力。真正有用的是 avg10、avg60、avg300 这三个滑动平均值。

那么,什么样的数值算「危险」?

我根据项目经验,给一个参考范围:

压力级别 some avg10 full avg10 系统表现
正常 < 1% < 0.3% 流畅,无感知
轻度 1% - 3% 0.3% - 1% 偶尔掉帧
中度 3% - 10% 1% - 5% 明显卡顿
严重 > 10% > 5% 系统几乎无响应

当然,这个表不是绝对的。不同硬件、不同场景下,感受会不一样。比如游戏场景下,用户对卡顿更敏感,阈值就要设得更低。

最后说一个实用技巧。

如果你想在代码里监控 PSI,可以用 Android 的 SystemPressureManager API。但更直接的方式是轮询读取 /proc/pressure/memory 文件。我一般用 1 秒的间隔,取 avg10 的值做判断。

// 伪代码示例
while (true) {
    String psi = readFile("/proc/pressure/memory");
    float someAvg10 = parseSomeAvg10(psi);
    if (someAvg10 > 5.0f) {
        // 触发内存回收或杀进程
        triggerMemoryReclaim();
    }
    Thread.sleep(1000);
}

嗯,这里要注意:轮询太频繁会增加 CPU 开销,得不偿失。1 秒一次足够了。

避坑指南:我曾经把轮询间隔设成 100ms,结果 PSI 监控本身成了 CPU 压力的来源。真是讽刺——监控压力的工具,自己制造了压力。

总结一下:PSI 是系统压力的「晴雨表」,LMKD 是「急救医生」。晴雨表报得准,医生才能对症下药。理解 PSI 的指标含义,是调好内存阈值的第一步。