Linux:内存占用过高,却找不到对应进程 —— VMware 气球(Ballooning)

郎家岭伯爵 2026年06月26日 10次浏览

背景

近期遇到一个问题:一台 Linux 服务器告警(VMware 虚拟机):内存使用率长期偏高。登录后用 top 查看,used 已达 12 GB / 15.8 GB,但把进程列表里所有 RES 加起来不过 2~2.5 GB,剩下约 10 GB 内存"凭空消失"——从任何进程视角都找不到是谁占的(实则是 Linux 虚拟机专有的 气球内存 导致的),记录下问题定位过程。

解决

问题解决

问题描述

使用 top 命令看到 used 内存占用很高,但下面各个进程的内存占用很低:

MiB Mem : 15830.8 total, 730.1 free, 12174.0 used, 2926.7 buff/cache

进程 RES 求和 ≈ 2.5 GB,与 used 12 GB 存在 ~10 GB 缺口top 默认只统计进程用户态驻留内存,下面几类内存不归任何进程:内核 Slab、共享内存/tmpfs、页表、HugePages、内核直接申请的裸页

使用 free -h 同样可以看到内存占用很高。

关键验证:内存去向核算(/proc/meminfo)

查看内存占用情况:

cat /proc/meminfo | egrep 'MemTotal|MemFree|MemAvailable|Buffers|Cached|Shmem|Slab|SReclaimable|SUnreclaim|KernelStack|PageTables|VmallocUsed|HugePages_Total|AnonPages|Mapped'
AnonPages:   1328972 kB  (~1.27 GB)   ← 所有进程真正吃的匿名内存
Cached:      2695816 kB  (~2.57 GB)   ← 可回收文件缓存
Slab:         448552 kB  (~0.43 GB)
PageTables:    22192 kB
VmallocUsed:       0 kB
HugePages_Total:   0

可核算合计仅 ≈ 5 GB,而 MemTotal 15.6 GB —— 缺口约 10 GB,所有标准字段都不认账

尝试手动回收缓存占用的内存

# 手动回收缓存占用的内存
sync && echo 3 > /proc/sys/vm/drop_caches
指标 执行前 执行后
used 12179 11949(仅 ↓230 MB)
buff/cache 2927 427
free 723 3454

缓存被成功回收,但 used 几乎不动 —— 证明这 10 GB 是不可回收的硬占用,不是缓存。

其实从 top 命令里也可以看出,这并不是 buff/cache 占用的内存,两者相差太多了。

确定问题:确认虚拟化 + 气球驱动

查看操作系统类型

systemd-detect-virt

输出:

vmware

确认是 VMware 虚拟化的操作系统(如果是其它类型的虚拟机也会有相应的输出),而非物理机(如果是物理机会输出 none)。

确认是否带有气球驱动

lsmod | egrep -i 'balloon|vmw|virtio'

输出:

vmw_vsock_virtio_transport_common    32768  1 vsock_loopback
vmw_vsock_vmci_transport    32768  1
vsock                  45056  5 vmw_vsock_virtio_transport_common,vsock_loopback,vmw_vsock_vmci_transport
vmw_balloon            24576  0
vmw_vmci               86016  2 vmw_balloon,vmw_vsock_vmci_transport
vmwgfx                364544  2
drm_kms_helper        217088  1 vmwgfx
ttm                   110592  1 vmwgfx
drm                   557056  5 vmwgfx,drm_kms_helper,ttm
vmw_pvscsi             28672  4

确认存在气球驱动。

确认气球驱动占用的内存

vmware-toolbox-cmd stat balloon

输出:

10290 MB

至此,找到“丢失”的内存,的确是气球驱动占用了内存

理论部分

什么是"吹气球"(Memory Ballooning)?

在虚拟化中,宿主机(ESXi)往往内存超分(overcommit)——分配给所有虚拟机的内存总和超过物理内存。当宿主机内存吃紧时,它需要从某些"用得不满"的 VM 里回收内存,分给更需要的 VM。

机制如下:每台装了 VMware Tools 的 guest 内都有一个气球驱动 vmw_balloon。宿主机命令它"充气",驱动就在 guest 内部调用 alloc_pages 申请大量物理页并锁住,再把这些页对应的物理内存交还给宿主机。

形象地说:

气球在 guest 里"占座",把座位(物理页)腾给宿主机去分配给别的 VM。guest 自己看:内存被"用掉"了;但用它的不是任何应用,而是这只"气球"。

这正是本例 10290 MB 的去向。

为什么从进程视角看不到占用?

气球占走的页是内核驱动直接申请的裸页

  • 不属于任何用户进程 → top/ps 任何进程都不背锅;
  • 不是匿名页(anon)、文件页(file)、也不走 slab → /proc/meminfo/proc/vmstat 的标准字段都不计入;
  • 不可回收 → drop_caches 无效。

所以表现就是"used 很高,但谁都找不到"。

排查方向是如何确定的?

排查不是一上来就猜气球,而是逐层缩小范围

  1. 先核算内存去向:把 meminfo 各项与 used 对账,确认缺口确实存在且落在"非进程"区间。

  2. 判断可否回收drop_cachesused 不降 → 排除文件缓存,锁定"硬占用"。

  3. 缩小到内核态:vmstat 所有计数器求和 < 1 GB → 缺口既不在用户态也不在常规内核统计里,只能是内核直接申请的裸页

  4. 区分两大嫌疑(关键分叉):

    • 内核模块泄漏(如安全 EDR agent 的 hook 模块通过 alloc_pages 泄漏);
    • 气球驱动占用(虚拟机场景特有)。

    两者现象完全相同,必须用 systemd-detect-virt + lsmod | egrep -i 'balloon|vmw|virtio' 区分。本例命中 VMware + 活动的 vmw_balloon,再用 vmware-toolbox-cmd stat balloon 拿到精确数值实锤。

最终内存对账

数值
气球占用 (balloon) 10,290 MB
进程匿名内存 (anon) ~354 MB
Slab + 页表 + kernel_misc ~480 MB
残余 buff/cache ~427 MB
合计 ≈ 11.5 GB ≈ used 11949 MB

账完全对上,排查闭环。


总结

  • 根因:这台 VM 被 ESXi 宿主机通过 vmw_balloon 回收(ballooning)了约 10.3 GB 内存,导致 guest 内 used 虚高、看似"内存丢失"。本机没有内存泄漏、没有问题进程,无需也无法自行修复。

  • 本机不要做的事drop_caches、kill 进程、调内核参数均无效(气球页不可回收);重启 VM 只能暂时缓解,宿主机一压力气球会重新充起。

  • 正确处置(交给虚拟化/平台团队):

    1. 检查 ESXi 宿主机内存使用与 balloon/swap 压力,确认是否超分;
    2. 给本 VM 配置内存预留(Memory Reservation),保护其不被吹气球;
    3. 给宿主机扩容物理内存,或用 vMotion 迁移部分 VM 分担压力。
  • 长期监控:盯住一个值即可

    vmware-toolbox-cmd stat balloon
    

    持续走高 = 宿主机内存压力加大(应用真要用内存而气球不放气时会触发 swap / 性能抖动);回落到接近 0 = 内存已自动还回。

  • 方法论沉淀:遇到"used 高但进程找不到",按 meminfo 对账 → drop_caches 验回收 → vmstat 缩到内核态 → 区分泄漏/气球 的顺序排查;在虚拟机上,永远记得把 Ballooning 列为首要嫌疑之一。