一次内存“卡顿”全流程实战分析:从制造问题到优化解决


📖 推荐阅读:《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) // 8MB

int 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);  // 10ms
        free(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_zonetry_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_zonekswapdtry_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);  // 预分配8MB
        memset(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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值