一条注释引发的血案:我的服务器为什么每次上传文件就卡死
一、案发现场
事情是这样的:我的腾讯云轻量服务器(2G 内存)有个毛病——每次上传、保存文件的时候,它就卡死。不是慢,是彻底僵住,SSH 都连不上,只能去控制台点重启。
重启完一切正常,用着用着,再上传个大文件,又死。如此循环,我一度以为这台机器中了邪。
二、第一反应:肯定是内存爆了
上传大文件卡死,第一直觉就是内存。于是登上去翻内核日志找 OOM 记录——
结果一条都没有。
这下有点懵。如果真是内存耗尽被 OOM Killer 杀进程,日志里一定会写 Out of memory: Killed process。可它干干净净,什么都没有。
后来才想明白:它不是被杀死的,是被活活憋死的。
三、转折点:一行被 systemd 嫌弃的注释
翻日志时,我注意到一条被我忽略很久的警告:
Invalid memory limit '512M # 硬上限: 超过直接 OOM kill...', ignoring看懂这行的时候,我整个人是裂开的。
我明明给两个 Java 服务都配了内存限制 512M,但 systemd 不支持行尾注释。它把 # 后面的中文说明当成了数值的一部分,512M # 硬上限... 当然不是合法数字,于是 systemd 一句 "ignoring",把我的限制直接扔了。
我以为是保险丝,其实那保险丝从装上去那天就没接过线。
验证了一下,果然:
MemoryMax=infinity ← 本该是 512M四、真凶:三个"大胃王"和一块快要撑爆的 swap
顺着查下去,画面就很清楚了:
pitlog.jar 692M ████████████████
ydt-server 320M ████████
mysqld 219M █████
────────────────────────────────
总内存 1.9G,available 只剩 292M
Swap 1G,已经吃掉 582M更要命的是 vmstat 的第一行:
si=112 so=585si/so 非零,就是系统正在疯狂把内存往硬盘上倒腾(换页)。
完整的事故链条是这样的:
配置失效 → 两个 JVM 裸奔、没有堆上限 → 内存被吃光 → 上传时堆瞬间膨胀,缺口爆掉 → 系统拼命往 swap 换页 → swap 在磁盘上,IO 一堵整机就僵 → SSH 想登进来要新开进程,内存分配不到,直接登不上 → 只能硬重启。
所以日志里没有 OOM 记录——它压根没走到"杀进程"那一步,是在换页的泥潭里活活卡死的。
五、修复:给两道门都上锁
改起来其实就三步:
第一步,把注释挪到单独一行(这个坑,我是真的一脚踩满):
[Service]
# 硬上限,留足系统缓冲
MemoryMax=700M
MemorySwapMax=1500M第二步,给 JVM 也设死堆上限。systemd 限的是整个进程,JVM 堆不单独限,它会在上限里面顶到顶:
-Xms384m -Xmx650m -XX:MaxMetaspaceSize=160m -XX:+UseSerialGC小内存机器上 UseSerialGC 是真香,G1 光自己就要吃掉上百兆。
第三步,重启验证:
MemoryMax=734003200 ← 700MiB,生效了
-Xmx650m ← 堆也限住了修完的对比:
| 修复前 | 修复后 | |
|---|---|---|
| 内存限制 | infinity | 700M / 900M ✅ |
| JVM 堆 | 没设 | -Xmx 已设 ✅ |
| 可用内存 | 292M | 500M+ ✅ |
| 最坏情况 | 整机宕机,硬重启 | 单进程被 kill,10 秒自动拉起 |
六、四句话总结这次学到的
- systemd 配置文件不支持行尾注释。 一行
#,能让你的内存限制静默失效、毫无提示地裸奔几个月。 - 没有 OOM 日志 ≠ 不是内存问题。 换页颠簸能把机器憋死在"杀进程"之前,日志里连尸体都找不到。
- "配了"不等于"生效了"。 验证一定要落到
systemctl show的实际数值上,别信配置文件里写着什么。 - 2G 机器跑双 Spring Boot + MySQL + Docker,本来就是在走钢丝。 限制只是让摔下来不致命,真想睡安稳觉,还是得升到 4G。
排查顺序也可以记一下:dmesg 找 OOM 记录 → free 看 available → vmstat 看 si/so → ps 排序看谁在吃。四步走完,病因基本就藏在里面了。