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)。它的逻辑是:
- 监控 PSI 内存压力指标
- 当压力超过阈值时,触发不同级别的回收或杀进程
- 压力越大,动作越激进
说白了,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 的指标含义,是调好内存阈值的第一步。