← 返回信息流
AI 资讯Hacker News·2 小时前

FreeBSD 疯狂吞噬我的内存

原标题:FreeBSD Ate My RAM

速览

有用户报告称 FreeBSD 操作系统出现严重内存占用问题,系统可用内存被急剧消耗。该问题可能由特定内核组件或驱动程序引起,导致系统性能下降。目前社区正在排查原因,尚无官方修复方案。对于服务器和嵌入式环境而言,此类内存管理问题可能影响系统稳定性。

AI 深度解读

背景

一位开发者将自己的网站服务器从 Ubuntu 迁移到 FreeBSD 后,发现 fastfetch 和 btop 这两款系统监控工具报告的内存使用率存在巨大差异。fastfetch 显示已用内存高达 82%,而 btop 仅显示 7%。这一现象引发了他对现代操作系统内存报告机制的深入探究。文章最初发布在 Hacker News 上,后续作者通过源码分析、系统调用追踪等方式,试图解释 FreeBSD 内存使用率的“矛盾”表现。

核心内容

1. 为什么内存使用率难以定义?

现代操作系统都采用虚拟内存(VM)系统,将物理内存划分为 4KiB 大小的页面(page)。内核将这些页面放入不同队列,以在进程需要时分配内存,并在内存紧张时回收。FreeBSD 定义了以下页面队列类型(见 sys/vm/vm_page.h):

  • PQ_NONEPQ_UNSWAPPABLE:通常被“锁定”在内存中,不可被交换出去,例如内核自身使用的内存。
  • PQ_ACTIVE:被用户态进程当前活跃访问的页面。
  • PQ_INACTIVE:一段时间未被访问的页面,可以被回收或换出。
  • PQ_LAUNDRY:等待被写入交换空间(swap)的页面。当系统需要空闲页面时,会先将 inactive 页面移入 laundry 队列,再将其写回磁盘。
  • PQ_FREE:完全未使用的页面。

通过 top 命令可以看到 FreeBSD 将这些队列报告为 activeinactivelaundrywiredfree 等类别。关键问题是:inactive 页面虽然目前未被使用,但可以被内核随时回收(重新变为 free),因此它们是否应算作“可用”内存? 同样,wired 内存中的一部分(例如文件系统缓存)也是可回收的。这导致不同工具采用不同的启发式算法来计算“已用内存”。

2. 磁盘缓存:ARC 和 ZFS

FreeBSD 的默认文件系统 ZFS 自带自适应替换缓存(ARC,Adaptive Replacement Cache),它会在内存中缓存最近使用的磁盘数据,以加速重复读取。ARC 是 wired 内存的一部分,但会在系统内存不足时自动收缩。用户可以通过 sysctl kstat.zfs.misc.arcstats 查看 ARC 大小、最小/最大配置等参数。例如:

sysctl -n kstat.zfs.misc.arcstats.size | gnumfmt --to=iec   # 输出如 3.1G

这直接解释了为何 top 显示内存使用率高——大量 wired 内存被 ARC 占用,但本质上它们是可回收的缓存。

3. fastfetch、btop 和 htop 的不同报告逻辑

作者深入分析了各个工具的源码,发现它们对“已用内存”的计算方式完全不同:

  • fastfetch 的启发式:
    free memory = free + inactive + cache*
    used memory = total - free memory
    它将 inactive 和缓存都视为可用内存,因此已用内存 = total - (free + inactive + 缓存)。在作者的 ThinkPad X230(FreeBSD 15.0-RELEASE)上,显示已用内存高达 82%。

  • btop 的启发式:
    available memory = total memory - active - wired
    free memory = free
    used memory = active + wired
    它将 wired 内存全部视为已用,忽略了其中包含的大量可回收缓存,因此已用仅 7%。

  • htop 的启发式:
    used memory = wired + active + laundry
    在作者的机器上显示 4.49G/5.69G(约 79%)。

4. btop 的内存报告在 FreeBSD 上存在明显缺陷

作者进一步检查了 btop 源码中的 src/freebsd/btop_collect.cpp,发现其通过 sysctlvm.stats.vm.* 获取各个队列的页面数:

  • v_active_count → active 页数
  • v_wire_count → wired 页数
  • v_cache_count → cached 页数(注意:这是旧的“缓存页”计数,并非 ARC!)
  • v_free_count → free 页数

然后它将 used = memWire + memActiveavailable = total - memActive - memWire,并将 v_cache_count 乘以页面大小后当作 cached 显示。但问题在于:在 FreeBSD 上,vm.stats.vm.v_cache_count 早已被废弃(deprecated),实际值始终为 0(因为现代 FreeBSD 使用 ARC 而非旧的 buffer cache)。因此 btop 的 cached 始终为 0,且它的 available 计算完全忽略了 ARC 等可回收缓存,导致内存使用率被极度低估(仅 7%)。这解释了为何 btop 的显示与其他工具差异最大。

5. 作者提供的综合脚本

作者编写了一个 Python 脚本,同时使用所有三种启发式算法(fastfetch、btop、htop)显示内存使用情况,以直观对比。截图显示 btop 的“used”远低于其他两项,验证了其计算错误。

关键要点

  • 现代操作系统内存管理复杂:内核通过虚拟内存页面队列管理物理内存,队列包括 active、inactive、laundry、wired 和 free。inactive 和 wired 中可回收的缓存部分,本质上可视为“可用”内存,但不同工具有不同定义。
  • FreeBSD 的 ZFS 使用 ARC 做磁盘缓存:ARC 占用了大量 wired 内存,但它会随内存需求自动收缩,是典型的“用则占、放则释”。
  • fastfetch 与 btop 的差异根源在于对 wired 和 inactive 的处理:fastfetch 将 inactive 和缓存都视为可用,因此报告的使用率较高(接近真实占用);btop 将 wired 视为不可回收,忽略了 ARC,因此严重低估使用率(仅 7%)。
  • btop 在 FreeBSD 上的实现有 Bug:它依赖于已废弃的 vm.stats.vm.v_cache_count,该值始终为 0,导致缓存统计缺失,进而影响 available 计算。
  • 没有绝对正确的内存使用率:不同工具服务于不同场景。系统管理员可能更关心 active + wired(不可轻易回收),普通用户可能希望看到能反映真实“占用”的数字(包括可回收缓存)。关键在于理解每种启发式的含义。
  • 作者建议:如果要在 FreeBSD 上准确监控内存,应首选 fastfetch 或 htop,并明确其计算规则;btop 的 FreeBSD 版本需要修复对 ARC 的识别。

意义与影响

  1. 对于系统管理员和开发者:该文章揭示了操作系统内存统计的复杂性,提醒用户不要仅仅依赖单一工具的数字,而应理解工具背后的算法。特别是在 FreeBSD 上,由于 ZFS 和 ARC 的广泛使用,不同工具的差异可能极大。
  2. 对 btop 项目:文章指出了 btop 在 FreeBSD 平台上的一个明确 bug,有助于推动项目修复。btop 当前依赖已废弃的 sysctl 变量,应改为利用 vm.stats.vm.v_inactive_target 或直接读取 ARC 统计(如 kstat.zfs.misc.arcstats.size)来评估可用内存。
  3. 对内存报告标准的思考:文章无意中引发了对“什么是已用/可用内存”的讨论。Linux 的 MemAvailable 已经给出了一个内核官方估计值,但 FreeBSD 目前缺乏统一标准。未来 FreeBSD 内核或许应提供类似 vm.stats.vm.v_available 的 sysctl,以便用户态工具统一参考。
  4. 对用户的意义:当你在 FreeBSD 上看到内存使用率飙升时,不必惊慌,大部分可能只是 ARC 缓存。同样,看到使用率极低时,也要警惕工具是否忽略了不可见的内存消耗。文章提供了一个 PHP(Python)脚本,让用户可一次性对比多种计算
查看原文 →crocidb.com