香港VPS的内存占用一旦飙高,很多人的第一反应是“商家超售了”。这个判断来得快,却未必对。超售确实是低价VPS市场的普遍现象,但把内存问题一概归咎于商家,容易忽略另一个同样常见的真相:你自己的程序,可能正在以你不曾察觉的方式消耗内存。两者指向的解决路径截然不同——前者需要举证、交涉甚至迁移,后者需要你改代码、调配置。判断错了方向,浪费的不只是时间。
要厘清这个问题,得先放下“used高就是有问题”的直觉。Linux的内存管理逻辑和Windows不同,它不会让空闲内存闲着。内核会拿空闲内存去缓存磁盘文件,加速后续读取。所以 free -h 里 used 列的数字偏高,很多时候只是缓存占了位置,并不代表系统真的缺内存。真正该看的是 available 列,那是内核估算的、在不触发交换的前提下还能分配给新进程的内存量。如果 available 还有充足余量,used 再高也不必慌张。
但如果你发现 available 持续萎缩,系统开始频繁使用 swap,甚至出现进程被强制杀死的记录,那就确实有问题了。问题出在谁身上,需要往下拆。
先排除最基础的误解:你的内存真的“少”了吗
有一种情况与超售和内存泄漏都无关:你买的是2GB内存,系统显示的总量却只有1.8GB左右。这是正常的。虚拟化层和内核本身需要预留一部分内存,KVM架构下通常有百分之几的损耗。只要总内存与标称值的差距在合理范围内,就不构成超售的证据。
真正值得警惕的信号是另一种:总内存显示正常,但你几乎没跑什么像样的业务,available 就已经低得可怜。比如一台标称2GB的机器,刚装完系统、只跑了Nginx和MySQL,available 就只剩一百多兆。这时候执行 ps aux --sort=-%mem | head -10,看看排在前面的进程加起来占了多少。如果所有进程的 RSS 总和远远填不满那2GB,而可用内存却所剩无几,差额去了哪里?一种可能是内核的 slab 占用异常,另一种可能就是宿主机层面的内存气球(ballooning)机制在起作用——物理机内存紧张时,宿主机动态“借走”了已经分配给虚拟机的部分内存。这种情况下,你的VPS总内存看起来没变,实际能用的却被悄悄抽走了。
不过,仅凭这一点还不能下定论。更常见的情形是,进程列表里确实有一个家伙,安安静静地占着几百兆甚至上G的内存,而你觉得“它本来就应该占这么多”。
程序的问题,往往藏在“正常”的表象下
内存泄漏的定义不是“某个进程占了很多内存”。数据库占几个G、Java堆开得大、编译任务吃掉大量内存,这些都是合理的。不正常的是另一种模式:在一个负载大致稳定的时段里,某个进程的常驻内存(RES/RSS)持续单调增长,爬上去就下不来,重启后暂时回落,然后继续爬。
判断方法不需要多复杂的工具。用 ps aux --sort=-rss | head -20 找到占用最高的几个进程,记下PID和当前的RSS值。等半小时到一小时,在业务量没有明显变化的前提下再跑一次同样的命令。如果同一个PID的RSS涨了一截且没有回落的迹象,这就是需要警惕的信号。更精细一点,可以看 /proc/<pid>/status 里的 VmRSS 字段,或者用 smaps_rollup 看 Pss,后者对于多进程程序(比如PHP-FPM池、Node.js cluster)更能反映真实分摊后的内存占用。
不同运行时环境的排查手段各有侧重。如果是C/C++程序,valgrind --leak-check=full 是定位泄漏点的经典工具,能给出未释放分配的调用栈。Node.js 应用可以通过定时打印 process.memoryUsage() 观察 heapUsed 是否单调增长,必要时用 heapdump 抓取堆快照做对比。Python 应用则可以用 tracemalloc 或 objgraph 追踪对象的增长趋势。关键在于,你要先确认那个持续增长的内存确实属于你的应用进程,而不是内核或其他系统组件。
还有一个容易被忽略的来源:配置不当。MySQL 的 innodb_buffer_pool_size 在1GB内存的机器上被设成768MB,PHP-FPM 的 pm.max_children 开得太大导致子进程数量失控,这些都会让内存以“正常业务”的名义被吃掉。这类问题不算泄漏,但同样是你自己这边的事,怪不到商家头上。
超售的痕迹:不在进程列表里,而在系统行为中
如果进程侧排查干净了,没有任何一个进程表现出异常增长,所有进程的RSS总和也远低于总内存,但 available 就是持续紧张,swap 频繁读写,这时候超售的嫌疑才真正值得认真对待。
内存超售在KVM架构下的典型表现是气球驱动(ballooning)的介入。宿主机发现物理内存不够分给所有虚拟机了,就通过气球驱动向你的VPS“回收”一部分已分配但暂时未被充分使用的内存。你的 free -h 里总内存不变,但 available 会莫名其妙地缩水,且缩水幅度与你的实际负载不匹配。更直接的证据在 dmesg 里:如果你看到内核频繁进行内存回收的记录,或者在没有跑重负载的情况下出现 OOM Killer 杀掉进程,而进程列表里又没有明显的内存大户,那就很可能是宿主机层面的内存压力传导到了你的实例上。
另一个辅助判断来自 CPU 的 steal time。在 top 命令的输出里,%Cpu(s) 行有一个 st 列,代表虚拟CPU等待物理CPU的时间比例。如果 st 长期高于5%,说明宿主机上的邻居们正在争抢CPU资源,这台物理机的负载已经相当饱和。虽然 steal 高不直接等于内存超售,但它是一个旁证:一台连CPU都被挤到 steal 飙升的物理机,内存分配上大概率也不会太宽裕。你可以在晚高峰时段用 vmstat 1 5 连续采样,观察 st 列是否稳定偏高。
磁盘 I/O 是另一个容易被忽略的旁证。超售严重的机器,存储后端往往也是共享且过载的。用 dd if=/dev/zero of=./testfile bs=1M count=1024 conv=fdatasync 做一次顺序写测试,如果写入速度远低于同价位SSD VPS的正常水平,且多次测试波动剧烈,说明这块盘后面不止你一个人在用。一个在CPU、内存、磁盘三个维度上都表现出争抢痕迹的VPS,内存超售的可能性自然更高。
判断流程:
第一步,确认 available 的真实状态。 执行 free -h,如果 available 还有总内存的20%以上,且 swap 的 used 长期为0,那么内存高占用大概率是缓存行为,不需要进一步动作。如果 available 持续低于10%,进入第二步。
第二步,检查进程侧。 用 ps aux --sort=-%mem | head -10 看前十个进程的内存占用总和。如果这个总和接近甚至超过了总内存,问题在你的程序上。如果总和远低于总内存(比如2GB的机器,所有进程RSS加起来不到800MB),而 available 却只剩几十兆,进入第三步。
第三步,看内核和系统层面的证据。 执行 dmesg | grep -i "out of memory" 和 dmesg | grep -i "killed process"。如果有OOM记录,但触发OOM的那个进程本身内存并不大(说明系统是在可用内存已经极低的情况下才杀的它),这指向外部因素。同时观察 vmstat 1 5 的 si/so 列,如果 swap 换入换出持续活跃,而你的应用并没有在大量分配内存,说明系统在替别人的内存压力买单。
第四步,做一次排除性测试。 在业务低峰期,停掉你所有非必要的应用进程,只保留SSH和基础系统服务,然后观察 free -h 的 available 是否恢复到接近总内存的水平。如果停掉自己的程序后 available 大幅回升,说明内存确实是被你的程序吃掉的。如果你的程序全停了,available 依然被什么东西占着不释放,那问题就不在你这边。
总结:香港VPS的内存问题,很少是非黑即白的。商家的超售是客观存在的行业现实,但你的程序也完全有可能在不经意间把内存吃干抹净。最忌讳的做法是一看到 used 高就找客服吵架,或者一遇到卡顿就加钱升级配置——前者可能冤枉了商家,后者可能只是用更大的内存掩盖了一个本该修复的泄漏。先看 available,再看进程趋势,最后看系统层面的异常信号。三步走完,答案通常就清楚了。内存不会无缘无故消失,它总得有个去处。找到那个去处,问题就解决了一半。
推荐文章