[内核内存] [arm64] 内存回收机制深度解析---从shrink_node到页面回收实战

张开发
2026/4/14 19:33:14 15 分钟阅读

分享文章

[内核内存] [arm64] 内存回收机制深度解析---从shrink_node到页面回收实战
1. ARM64内存回收机制全景解读当你的手机开始卡顿或者服务器突然响应变慢时背后很可能正在上演一场惊心动魄的内存保卫战。在ARM64架构的Linux系统中内存回收机制就像一位精明的仓库管理员它需要在有限的空间里不断整理、淘汰旧物资确保新货物有地方存放。今天我们就来揭开这个机制的神秘面纱从最核心的shrink_node函数出发直击页面回收的每一个技术细节。内存回收机制本质上是在处理三个关键矛盾内存供需平衡、回收效率和系统性能影响。当系统检测到内存不足时会触发两种回收方式一种是后台的kswapd守护进程进行的异步回收另一种是当进程分配内存时直接触发的同步回收。ARM64架构下的实现有几个显著特点采用NUMA-aware的设计每个内存节点独立管理回收针对大页HugePage做了特殊优化处理利用硬件特性加速页面访问状态检测交换空间管理考虑了移动设备特性在内存压力较大时系统会按照最近最少使用原则通过扫描LRU链表来识别可回收页面。整个过程就像整理衣柜把最近常穿的衣服放在顺手位置不常穿的收起来完全用不到的考虑捐赠释放。2. shrink_node函数深度剖析2.1 函数执行流程解析shrink_node是内存回收的核心引擎它的工作方式就像一位高效的仓库管理员static bool shrink_node(pg_data_t *pgdat, struct scan_control *sc) { do { // 外层循环对整个节点进行扫描回收 memcg mem_cgroup_iter(root, NULL, reclaim); do { // 内层循环处理每个memcg子系统 shrink_node_memcg(pgdat, memcg, sc, lru_pages); shrink_slab(sc-gfp_mask, pgdat-node_id, memcg, sc-nr_scanned - scanned, lru_pages); } while ((memcg mem_cgroup_iter(root, memcg, reclaim))); if (global_reclaim(sc)) shrink_slab(...); } while (should_continue_reclaim(pgdat, ...)); }这个双重循环结构体现了Linux内存管理的层次化设计理念。外层循环确保对整个内存节点完成完整的回收操作内层循环则精细处理每个内存控制组memcg。这种设计带来了三个优势资源分配更公平防止单个memcg占用过多回收资源优先级处理更灵活可以根据memcg的重要性调整回收顺序统计信息更准确每个memcg的回收效果可以独立评估2.2 关键决策逻辑should_continue_reclaim函数是回收过程的大脑它决定了何时停止回收操作。其判断逻辑主要考虑已回收页数如果已经回收了足够多的页面sc-nr_reclaimed (2 sc-order)可以考虑停止可回收页存量当非活跃LRU链表中的页面太少时停止内存规整机会检查是否可以通过内存规整compaction来满足需求实际开发中我们经常遇到的一个问题是如何判断回收是否充分可以通过以下指标评估/proc/vmstat中的pgscan_kswapd_*和pgsteal_kswapd_*计数器/proc/meminfo中的Active(file)和Inactive(file)比值dmesg中是否有kswapd0的活跃日志3. shrink_node_memcg实现细节3.1 扫描策略制定get_scan_count函数就像一位精算师它计算每个LRU链表应该扫描多少页面。其决策矩阵如下条件扫描策略无交换空间仅扫描文件页swappiness0仅扫描文件页内存极度紧张均衡扫描匿名/文件页文件缓存充足优先扫描文件页这个策略在ARM64设备上尤为重要因为移动设备通常交换空间有限zRAM压缩比是关键文件缓存对用户体验影响大电池续航需要考虑回收能耗3.2 页面分类处理shrink_list函数是真正的执行者它根据页面类型采取不同策略static unsigned long shrink_list(enum lru_list lru,...) { if (is_active_lru(lru)) { if (inactive_list_is_low(...)) shrink_active_list(...); } else { return shrink_inactive_list(...); } }活跃链表处理就像整理常用物品检查对应的非活跃链表是否库存不足将部分活跃页面降级到非活跃链表保留最近访问的可执行文件缓存非活跃链表处理则像清理废旧物资解除页面映射unmap回写脏页writeback最终释放到伙伴系统4. 页面回收核心流程4.1 匿名页回收全流程匿名页回收就像处理私人日记——需要特殊照顾交换空间分配通过add_to_swap为没有备份位置的页面分配交换槽位解除映射使用try_to_unmap清除所有PTE映射数据回写将页面内容写入交换分区可能到zRAM缓存清理从swap cache中移除最终释放页面回到伙伴系统在ARM64平台上这个过程有几个优化点利用CONT页表特性批量处理映射针对大页HugeTLB的特殊处理与CPU缓存刷新指令协同工作4.2 文件页回收机制文件页回收相对简单就像处理复印件引用检查通过page_check_references评估页面价值缓存清理从page cache中移除数据回写仅对脏页执行且策略更保守立即释放因为数据有磁盘备份实际性能优化中我们经常调整/proc/sys/vm/vfs_cache_pressure默认100/proc/sys/vm/dirty_writeback_centisecs默认500文件系统的mount参数如noatime5. 高级优化技巧5.1 LRU链表平衡策略inactive_list_is_low函数维护着LRU链表的动态平衡其核心算法是inactive_ratio sqrt(内存大小(GB) * 10) 理想不活跃页数 总页数 / (inactive_ratio 1)这个设计确保了小内存设备保持较高的活跃页比例大内存服务器允许更多页面处于非活跃状态比例随内存大小非线性变化5.2 直接回收优化当进程触发直接回收时系统会采取更积极的策略扫描量增加scan_adjused标志会让回收更多页面优先级提升减少睡眠时间加快回收速度提前唤醒kswapd协同工作提高效率在Android系统中这个机制经常与lowmemorykiller协同工作形成完整的内存应急方案。6. 实战问题排查6.1 常见性能问题回收风暴表现为kswapd持续高CPU检查/proc/vmstat中的pgscan_kswapd_*激增解决方案调整swappiness或增加内存过早OOM回收未能释放足够内存检查shrink_page_list的回收效率解决方案优化应用内存使用模式IO瓶颈回收导致设备IO饱和观察vmstat中的io指标解决方案调整dirty_ratio或使用更快存储6.2 调试技巧动态追踪perf probe --add shrink_node perf stat -e probe:shrink_node -a关键指标监控watch -n 1 cat /proc/vmstat | grep -E pgsteal|pgscan页面状态检查tools/vm/page-types -l -N在ARM64服务器环境中还需要特别注意NUMA平衡问题可以使用numastat工具监控跨节点访问情况。理解内存回收机制对性能调优至关重要。就像一位经验丰富的系统管理员告诉我的当遇到内存问题时不要急着加内存先看看回收机制是否在高效工作。 通过深入理解shrink_node到页面回收的完整流程我们能够更精准地诊断内存问题制定有效的优化策略。

更多文章