理解 FreeBSD 内存报告与 ZFS ARC 缓存
理解 FreeBSD 内存报告与 ZFS ARC 缓存
FreeBSD 中的内存报告差异
FreeBSD 的内存报告在不同的系统监控工具中往往显得不一致,这是因为每个工具都采用了不同的启发式算法来定义“已使用”与“可用”内存。这主要是由于操作系统如何管理其虚拟内存 (VM) 系统,以及如何利用 RAM 进行磁盘缓存以优化性能。
在 FreeBSD 中,内存被分为几个页面队列:
- Active: 用户态进程当前正在使用的页面。
- Inactive: 一段时间未被访问的页面;这些页面是可回收的,如果系统需要更多内存,它们可以被释放。
- Laundry: 排队等待写入交换空间 (swap) 的页面。
- Wired: 内核或锁定进程使用的内存,无法被换出。这一类别还包括 ZFS Adaptive Replacement Cache (ARC)。
- Free: 完全未使用的内存。
由于不活跃页面 (inactive pages) 和部分已连接内存 (wired memory,如磁盘缓存) 可以被内核回收,因此工具必须决定是将这些计入“已使用”还是“空闲”。
ZFS ARC 在内存使用中的作用
ZFS 是大多数现代 FreeBSD 安装的默认文件系统,它使用 Adaptive Replacement Cache (ARC) 将最近使用的数据存储在 RAM 中。与标准的内核缓存不同,ARC 绕过了传统的 VM 系统,并存储在“wired”内存类别中。
由于 ARC 被设计为在系统需要更多内存供应用程序使用时自动缩小,因此它实际上是可回收的。然而,如果监控工具只是简单地将“active”和“wired”内存相加,ARC 缓存会使系统看起来比运行进程实际所需的 RAM 要多得多。
为了准确跟踪 ARC 使用情况,管理员可以使用 sysctl 查询特定的内核参数:
kstat.zfs.misc.arcstats.size: 当前缓存大小。kstat.zfs.misc.arcstats.c_min: 配置的最小缓存大小。kstat.zfs.misc.arcstats.c_max: 配置的最大缓存大小。
分析工具的启发式算法:btop, fastfetch, 和 htop
不同的工具使用不同的公式计算内存使用量,导致在同一台机器上出现冲突的报告。
btop
从历史上看,btop 使用一种简化的启发式算法:used memory = active + wired。这种方法在 FreeBSD 上通常是不准确的,因为它忽略了 ARC 缓存的可回收性质,并且未能考虑到不活跃页面 (inactive pages)。
fastfetch
fastfetch 使用了更具包容性的空闲内存定义:free memory = free + inactive + cache。因此,used memory = total - free memory。这通常提供了一个更乐观的可用资源视图。
htop
htop 通常分别报告内存类别,但计算已使用内存的方式为:used memory = wired + active + laundry。
btop 内存报告中的关键 Bug
对 btop 源代码 (src/freebsd/btop_collect.cpp) 的研究揭显示了影响 FreeBSD 用户的两个重大问题:
32 位整数溢出
btop 将内存页面计数存储在 u_int (无符号 32 位整数) 中。当通过将页面计数乘以页面大小来计算总字节数时,任何超过 4GiB (4,294,967,296 字节) 的值都会发生回绕。例如,一个使用 4.42 GiB 已用内存的系统可能会因为这种整数溢出而被报告为仅使用了 422 MiB。
遗留缓存报告
btop 尝试从 vm.stats.vm.v_cache_count 获取缓存数据。然而,自 FreeBSD 12.0 以来,该 sysctl 参数一直是一个“为了兼容性而存在的占位符 (dummy for compatibility)”,并且始终返回 0。实际的缓存队列自 FreeBSD 6.3.0 以来就不再存在了。
实现正确的内存跟踪
为了解决这些差异,内存监控工具必须明确考虑 ZFS ARC 和 FreeBSD 缓冲区缓存 (vfs.bufspace)。
计算已使用内存的修正逻辑涉及:
- 计算总缓存: 将
vfs.bufspace与 ARC 缓存的可变部分(当前大小减去最小大小)相加。 - 计算原始已用: 将
active和wired内存相加。 - 计算实际已用: 从原始已用内存中减去计算出的缓存:
usedBytes = (cachedBytes < rawUsed) ? rawUsed - cachedBytes : 0。
这些改进已作为 pull requests 提交给了 btop、fastfetch (在此处逻辑被扩展到了所有支持 ZFS 的系统,包括 Linux 和 NetBSD) 以及 htop,以确保 FreeBSD 生区系中的内存报告保持一致且准确。