一次内存“卡顿”全流程实战分析:从制造问题到优化解决
📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry
一次内存“卡顿”全流程实战分析:从制造问题到优化解决
让你真正学会用代码+工具定位和优化内存瓶颈!
一、实战目标
- 复现 UI 场景常见的“分配卡顿”问题
- 用压力脚本+perf+buddyinfo精准定位瓶颈
- 给出代码级的优化方案
二、复现场景:用 C 代码制造“内存碎片/卡顿”
1. 测试代码1:反复大块分配+释放(制造碎片)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ALLOC_SIZE (8*1024*1024) // 8MBint main() {for (int i = 0; i < 1000; i++) {char *p = (char*)malloc(ALLOC_SIZE);if (p == NULL) {printf("malloc failed at %d\n", i);break;}memset(p, 0, ALLOC_SIZE);usleep(10000); // 10msfree(p);if (i % 50 == 0) {printf("loop %d\n", i);system("cat /proc/buddyinfo | grep Normal");}}return 0;
}
用途:反复分配大块、释放,快速制造物理内存碎片,模拟 UI 切换频繁分配 buffer 的场景。
2. 压力脚本:多进程并发制造极端压力
# bash 脚本,同时启动 8 个压力进程
for i in $(seq 1 8); do./alloc_stress &
done
wait
三、采集分析数据
1. 采集 buddyinfo
持续运行脚本的同时,另开终端持续采样:
watch -n 1 "cat /proc/buddyinfo | grep Normal"
预期现象:
- 高阶 order 8/9 空闲很快减少,最终低阶页也下降,碎片化加重。
2. perf 采集热点
启动压力脚本期间,采集全局性能数据:
perf record -a -g -o perf.data
sleep 20 # 或直到压力脚本运行结束
perf report
3. 典型 perf report 日志片段
解读:
compact_zone
、try_to_free_pages
这类热点占比极高,说明内核忙于碎片整理和回收页,分配卡顿正是因为它们变成热点!- 用户进程的分配调用(
__alloc_pages_nodemask
)紧随其后,也说明分配处于“慢路径”。 - 如果看到
kswapd0
CPU 消耗高,系统分配已极度“亚健康”。
4. ftrace 精细跟踪分配慢路径(选做)
如需进一步精确分配慢点:
cd /sys/kernel/debug/tracing
echo function_graph > current_tracer
echo __alloc_pages_nodemask > set_graph_function
echo 1 > tracing_on
# 再次触发压力分配
sleep 3; echo 0 > tracing_on
cat trace | head -100
重点看:单次分配是否耗时明显增大,是否有 compact_zone
、kswapd
、try_to_free_pages
等分支出现。
四、问题现象与瓶颈总结
1. buddyinfo 实时变化
# 某时刻
Normal 12 8 2 0 0 0 0 0 0 0
- 高阶全部为0,大块分配一定卡顿甚至失败
- 低阶也变少,小分配都可能进入慢路径,频繁回收,UI响应变差
2. perf report 热点直指内存回收/整理
- 绝非 CPU 计算问题,根本是内存分配的回收/碎片瓶颈!
五、优化实战
1. 核心优化思路
-
尽量避免频繁大块分配/释放,采用全局/静态 buffer 复用策略
-
适当提升 min_free_kbytes(例如 64MB),让系统更“保守”地分配和回收
echo 65536 > /proc/sys/vm/min_free_kbytes
-
UI 场景下可提前分配并复用大缓存,减少 runtime malloc/free
2. 优化代码示例
推荐:全局缓冲区复用
static char *ui_cache_buf = NULL;void init_ui_buffer() {if (!ui_cache_buf) {ui_cache_buf = malloc(8 * 1024 * 1024); // 预分配8MBmemset(ui_cache_buf, 0, 8 * 1024 * 1024);}
}void use_ui_buffer() {// 每次只清空、重用,不再反复分配memset(ui_cache_buf, 0, 8 * 1024 * 1024);
}
配置优化
/proc/sys/vm/min_free_kbytes
适当增大- 业务代码避免频繁释放大 buffer
六、结语
- 用 C 代码+压力脚本,能精准复现和分析“分配卡顿”,这是最好的实战训练。
- perf/buddyinfo/ftrace 配合,能让你准确找到内存瓶颈,掌控调优主动权。
- 记住:合理的缓存复用+水位参数优化=系统不卡顿,体验大提升!
📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry