libvirt 虚拟机内存管理

十月 29, 2025 更新: 十月 11, 2026 [虚拟化与容器] #libvirt #ballooning

libvirt windows虚拟机内存占用过高

虚拟机内存策略

Ballooning 如何解决这个问题?

Ballooning 的作用是 建立 Guest 与 Host 之间的内存使用反馈通道:

libvirt/QEMU 支持 Windows 虚拟机的内存 ballooning(动态内存管理)

需要在 宿主机与客户机两边都正确配置

<memballoon model='virtio'>
  <stats period='10'/>
</memballoon>

实战:Linux guest 内存 32G 恒 100%,cockpit 与 htop 打架(2026-10 更新)

现象

一台 32G 的 Ubuntu 24.04 开发机(16 vCPU,跑了一年):

第一反应是"是不是没装 qemu-guest-agent / 没有 memballoon"——都装了且工作正常:

$ virsh dommemstat ubuntu.code
actual 33554432
unused 32681168      ← balloon 驱动提供,guest 空闲 31.2G
available 32781204
rss 33512528         ← QEMU 进程 RSS,涨满
swap_in/out、major/minor_fault、disk_caches ...   ← qemu-guest-agent 提供

$ virsh qemu-agent-command ubuntu.code '{"execute":"guest-ping"}'
{"return":{}}

cockpit 那张卡片的真实语义

解包前端(gunzip -c /usr/share/cockpit/machines/index.js.gz)找到采集逻辑:cockpit-machines 调用 org.libvirt.Domain 的 GetStats(flags=CPU_TOTAL|BALLOON|VCPU, 0),从返回里取 balloon.rss(QEMU 进程在宿主机上的 RSS),除以 XML 的 currentMemory:

内存占用 % = balloon.rss / currentMemory    ← 宿主机视角,不是 guest 视角

(busctl 实测掩码:1=STATE、2=CPU_TOTAL、4=BALLOON、8=VCPU、16=BLOCK; virsh dommemstat 的 unused/available 才是 guest 视角。)

为什么 RSS 只涨不跌

三个事实叠加:

  1. guest 每摸过一页内存,这页就永久留在 QEMU 的 RSS 里——包括 page cache;
  2. guest 内核"闲着不如缓存",把 25G 拿去做 page cache,完全合理;
  3. balloon 从不自动归还:没有主动挤气球动作,QEMU RSS 只增不减。

于是 guest 用 6G、cache 25G、RSS 32G、cockpit 恒 100%——每一层都"没坏",是机制如此。

解法:freePageReporting(自动归还)

<memballoon model='virtio' freePageReporting='on'>
  <stats period='5'/>
</memballoon>

guest 内核周期性把空闲页报告给 QEMU(madvise DONTNEED),宿主机 RSS 自动回落; guest 需要时立刻拿回(弹性归还,不像 setmem 是硬上限)。要求:guest 内核 ≥5.7、 libvirt ≥6.10、QEMU 设备属性存在(qemu-system-x86_64 -device virtio-balloon-pci,help 可见 free-page-reporting)。生效需要关机再开机(memballoon 不能热改属性)。

对比:

virsh setmemfreePageReporting
语义硬上限(guest 可见内存变小)弹性归还(guest 内存不缩水)
生效立即,可逆需重启一次,之后全自动
cockpit 卡片仍 ~100%(分母同步变小)跟随真实占用动态变化

三个坑(define 静默剥离实录)

  1. freePageReporting 是 <memballoon> 的属性,不是子元素。写成子元素 <freePageReporting on='on'/> 时 virsh define 不报错但静默剥离——不带 --validate 的 define 不跑 RNG schema,宽松解析器直接忽略不认识的元素。加了 --validate 才会报 Extra element devices in interleave。
  2. 漏写 on='on' 也不行:<freePageReporting/> 解析为默认 off,格式化输出时省略, 同样静默消失。
  3. define 会顺带丢 <stats period>(balloon 统计采集周期,cockpit/dommemstat 依赖), define 后必须 virsh dumpxml --inactive <vm> 复查。

define 成功的判据不是命令退出码,而是复查存储结果:

virsh define --validate /path/to/vm.xml
virsh dumpxml --inactive <vm> | grep -A3 '<memballoon'   # 必须肉眼确认

重启后验证:

pgrep -f 'qemu-system.*<vm>' | head -1 | xargs -I{} cat /proc/{}/cmdline | tr '\0' '\n' | grep free-page-reporting
# → free-page-reporting=on

相关阅读