FreeBSD ate my RAM 摘要
作者在迁移 FreeBSD 服务器后,发现由于不同工具对内存计算的策略不同,RAM 占用率的显示存在显著差异。通过深入研究,揭示了现代操作系统内存管理的复杂性以及相关工具的实现逻辑。
虚拟内存管理系统
现代操作系统依靠虚拟内存(VM)系统管理物理内存,将其划分为 4KiB 的页面并分类存储在不同的队列中。在 FreeBSD 中,核心页面队列包括活跃(active)、不活跃(inactive)、洗衣(laundry)和不可交换(wired)。所谓的“空闲”内存仅仅是那些简单标记为释放的页面,而许多位于 inactive、wired 甚至部分内核内存中的页面,在系统需要时都是可以回收的。特别是磁盘缓存和 ZFS 的 ARC(自适应替换缓存),它们占据物理内存但高度可回收。
ZFS 与 ARC 缓存
FreeBSD 默认使用的 ZFS 文件系统使用 ARC 缓存来加速频繁读取的数据。该缓存由内核直接管理,不计入常规的缓冲区统计,但会占用系统总内存,并在内存不足时被优先释放。
工具间的差异与 Bug 详解
监控工具(如 btop、fastfetch、htop)对“已用内存”的定义各不相同。作者在分析 btop 代码时发现了两个导致显示错误的根本原因:
- 整数溢出:
btop 使用 32 位无符号整数(u_int)存储内存计数。在内存超过 4GB 时,数值会发生回绕,导致计算出的内存占用极其不准确。
- 依赖废弃参数:
btop 试图通过 vm.stats.vm.v_cache_count 获取缓存大小。但在 FreeBSD 12.0 及更高版本中,该参数仅作为“兼容性存根”返回 0,实际上不再提供真实的缓存数据。
修复与改进
针对上述问题,作者对 btop 进行了代码修正,并在 Pull Request 中提交:
- 变量精度提升: 将内存存储变量从 32 位升级为 64 位,防止溢出。
- 缓存数据源修正: 不再使用废弃的
v_cache_count。改为统计 vfs.bufspace(文件系统元数据缓存)和 kstat.zfs.misc.arcstats.size(ZFS ARC 缓存)。
- 逻辑调整: 在计算“已用内存”时,从
wired 内存中减去 ARC 缓存数据,确保显示的缓存内存是真正可回收的部分。
此外,作者还修复了 htop 中的类似问题,修正了未能正确处理 ARC 缓存的逻辑。对于 fastfetch,作者提供建议并获采纳。经过测试,这些修复使得工具能够更准确地反映 FreeBSD 的内存状态。