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

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


📖 推荐阅读:《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.xdnf.cn/news/15020.html

相关文章:

  • java中,stream的filter和list的removeIf筛选速度比较
  • JMM--数据原子操作
  • SpringAI与智能体入门
  • 解决kali Linux在VMware中的全局缩放问题
  • 量化可复用的UI评审标准(试验稿)
  • Python PyJWT详解:从入门到实战
  • 3dmax烘焙插件3dmax法线贴图烘焙教程glb和gltf元宇宙灯光效果图烘焙烘焙光影贴图支持VR渲染器
  • 针对工业触摸屏维修的系统指南和资源获取途径
  • NumPy-核心函数np.matmul()深入解析
  • CppCon 2018 学习:Surprises In Object Lifetime
  • 设计模式之访问者模式
  • 使用Langchain访问个人数据
  • SpringBoot系列—入门
  • PostgreSQL表操作
  • 深度学习5(深层神经网络 + 参数和超参数)
  • C++ 网络编程(15) 利用asio协程搭建异步服务器
  • 从数据洞察到设计创新:UI前端如何利用数字孪生提升用户体验?
  • 浏览器与服务器的交互
  • dify 1.5.1版本全面解析——知识库索引、动态参数及结构化输出插件全新登场
  • STM32的 syscalls.c 和 sysmem.c
  • 《C++初阶之类和对象》【经典案例:日期类】
  • RESTful API 安装使用教程
  • 黑马python(二十五)
  • Spring Boot + 本地部署大模型实现:优化与性能提升
  • 基于Linux的Spark本地模式环境搭建实验指南
  • 【github】想fork的项目变为私有副本
  • 项目介绍:LangGPT
  • Android View的绘制原理详解
  • 使用reactor-rabbitmq库监听Rabbitmq
  • Python 量化交易安装使用教程