4.7.2 内存分配与回收机制:伙伴系统与SLAB/SLUB
本节聚焦内核内存分配的两大机制:伙伴系统(页级分配)与 SLAB/SLUB(对象级缓存)。理解其协作方式,可用于定位碎片化、slab 膨胀、高阶页分配失败等问题。
原理草图:伙伴系统与 SLAB/SLUB 协作
伙伴系统要点
- 以 order 管理空闲链表,分配时拆分、回收时合并
- 适用于页框级别分配(如页缓存、匿名页、kmalloc 大对象)
- 外部碎片问题可通过内存紧缩与回收缓解
SLAB/SLUB 要点
- 为常用内核对象建立缓存,减少频繁分配释放的锁竞争
- SLAB:缓存含多个 slab,管理复杂但统计完善
- SLUB:结构更简化、内存占用更低、适合大规模并发
- 典型对象:inode、dentry、task_struct、buffer_head 等
观测与命令示例(含解释)#
1)查看伙伴系统碎片化
# 查看每个 NUMA 节点、各 order 空闲页数量
cat /proc/buddyinfo
# 示例输出(节选)
# Node 0, zone Normal 12 8 5 2 0 0 ...
解释: 数字从 order0(1页)到更高阶页,若高阶页长期为 0,说明碎片化严重。
2)查看 SLAB/SLUB 缓存
# 友好展示 slab 缓存占用与活跃对象
slabtop -o
# 查看原始 slab 统计
cat /proc/slabinfo | head -n 5
解释: slabtop 可快速发现占用异常的缓存(如 dentry、inode_cache)。
3)检测内存回收压力
# kswapd 频繁唤醒或 direct reclaim 增多会导致抖动
vmstat 1 5
解释: 关注 si/so(换入换出)、kswapd 相关指标,配合 dmesg 观察回收路径。
示例:模拟碎片化并观察高阶页不足#
步骤 1:安装工具(如 CentOS/RHEL)
# 安装 stress 工具
sudo yum install -y epel-release
sudo yum install -y stress
步骤 2:制造内存压力
# 启动 4 个内存压测进程,每个申请 256M
stress --vm 4 --vm-bytes 256M --vm-keep --timeout 30s
步骤 3:观察 buddyinfo 变化
cat /proc/buddyinfo
预期效果: 高阶 order 值下降或趋近 0,说明高阶页不足。
排错场景与处理建议#
场景 1:高阶页分配失败(dmesg 报错)
# 查看内核日志
dmesg | tail -n 50 | grep -i "page allocation failure"
处理:
- 启用内存紧缩(compaction)或临时降低 THP 需求
- 检查碎片化,尽量减少长时间持有大量小块内存
场景 2:slab 缓存占用异常
# 快速定位异常缓存
slabtop -o | head -n 10
处理:
- 排查是否有内核对象泄漏(如 dentry 暴涨)
- 检查应用是否频繁创建/销毁文件、连接、进程
参数与调优示例(可回滚)#
调整透明大页策略
# 临时关闭 THP(减少对高阶页的需求)
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
# 验证
cat /sys/kernel/mm/transparent_hugepage/enabled
启用内存紧缩(可提高高阶页可用性)
# 触发一次 compaction
echo 1 | sudo tee /proc/sys/vm/compact_memory
练习题(含目标与验证)#
练习 1:找出当前系统中占用最大的 slab 缓存
- 目标:识别前三位缓存
- 命令:
slabtop -o | head -n 15
- 验证:记录前三项的 name、objs、size。
练习 2:观察压力下的高阶页变化
- 目标:比较 stress 前后 /proc/buddyinfo
- 命令:
cat /proc/buddyinfo > /tmp/buddy.before
stress --vm 2 --vm-bytes 512M --vm-keep --timeout 20s
cat /proc/buddyinfo > /tmp/buddy.after
diff -u /tmp/buddy.before /tmp/buddy.after
- 验证:确认高阶页数量下降。
练习 3:定位 slab 增长原因
- 目标:分析 dentry/inode_cache 是否异常
- 命令:
slabtop -o | egrep "dentry|inode_cache"
lsof | wc -l
- 验证:若缓存高且打开文件数巨大,需排查应用文件句柄泄漏。