当前位置: 首页 > news >正文

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


📖 推荐阅读:《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_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);  // 预分配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



http://www.dtcms.com/a/267990.html

相关文章:

  • Apache Spark 4.0:将大数据分析提升到新的水平
  • 小架构step系列06:编译配置
  • 在C#中,可以不实例化一个类而直接调用其静态字段
  • 2025年03月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • python-转义字符
  • 李宏毅2025《机器学习》第四讲-Transformer架构的演进
  • 力扣971. 寻找图中是否存在路径【simple 拓扑排序/图 Java】
  • 【双向循环带头链表】
  • Java中的抽象类和接口
  • CICD[构建镜像]:构建django使用的docker镜像
  • 【9】用户接入与认证配置
  • 车载智能座舱用户画像系统研究二:子系统构建
  • Linux国产与国外进度对垒
  • GANs环境应用及启发思考
  • java学习——guava并发编程练习
  • 跨平台游戏引擎 Axmol-2.7.0 发布
  • @Data、@AllArgsConstructor、@NoArgsConstructor不生效。lombok不起作用怎么解决?
  • 设置LInux环境变量的方法和区别_Ubuntu/Centos
  • 网页怎么不使用工具截图和长图
  • 《Spring AI实战:Java智能开发速成指南》
  • D435i + ROS2
  • 利用selenium获取网页数据,脚本加载慢问题的解决办法
  • CentOS环境搭建-快速升级G++版本
  • 【CSS样式】有趣的滑块开关
  • 深入理解 classnames:React 动态类名管理的最佳实践
  • 20250706-6-Docker 快速入门(上)-镜像是什么?_笔记
  • TNNLS期刊接受后,最终版提交说明
  • QT - QCustomPlot
  • Next.js面试常问内容详解
  • 【网络安全基础】第八章---电子邮件安全