最可靠的第一步是检查dmesg中OOM Killer日志,再比对Nginx error.log中"exited on signal 9"等时间戳一致的异常信号;若有"Killed process nginx"输出且anon-rss达数GB,基本确认为内存耗尽被杀,需结合ps、/proc/PID/status及access.log请求特征进一步定位根因。

直接看 dmesg 里内核留下的“杀人记录”,再比对 Nginx error.log 中对应时间点的异常信号,这是最可靠的第一步。Nginx 自己不会写“OOM”三个字,但系统内核会如实记下谁被杀、为什么杀、杀了之后占了多少内存。
盯住 dmesg 里的 OOM Killer 日志
执行以下命令,确认是否真被内核杀掉:
-
dmesg -T | grep -i "killed process" | grep nginx—— 看是否有类似Out of memory: Kill process 12345 (nginx)的输出 - 如果有,记录下时间戳、被杀进程 PID 和
total-vm、anon-rss等关键内存数值(比如anon-rss:3943668kB表示实际占用近 4GB 物理内存) - 若无匹配结果,说明不是 OOM,可能是配置错误、段错误或主动退出,需另查
同步检查 Nginx error.log 中的信号线索
OOM 杀进程后,Nginx master 会记录 worker 异常退出,重点搜这些关键词:
-
worker process [0-9]+ exited on signal 9—— signal 9 就是 SIGKILL,几乎等同于被 OOM Killer 干掉 -
Killed或exited with code 137(137 = 128 + 9,也是 SIGKILL) - 按时间戳比对 dmesg 输出,锁定具体 worker PID 和发生时刻;若只看到
exited with code 0,基本可排除 OOM
查真实内存占用,别信估算值
用系统级命令看 RSS(常驻内存),而不是 VIRT 或 %MEM:
-
ps aux --sort=-%mem | head -10或top -o %MEM,重点关注 RSS 列 - 对可疑 worker PID(如 12345),执行:
cat /proc/12345/status | grep -E "VmRSS|Threads"cat /proc/12345/smaps | awk '/^Pss:/ {sum += $2} END {print sum " kB"}'—— PSS 更准确反映该进程实际内存开销 - 若运行在容器或 systemd 服务中,还要检查 cgroup 限制:
cat /sys/fs/cgroup/memory/$(systemd-cgtop | grep nginx | head -1 | awk '{print $1}')/memory.max 2>/dev/null
关联日志找触发请求特征
单看 error.log 不够,必须结合 access.log 锁定可疑流量:
- 用 dmesg 时间戳往前推 1–2 分钟,在 access.log 中统计高频 URI、User-Agent、IP 段
- 特别关注:
– 大量400 Bad Request且含request header too large—— 可能客户端发超大 header,Nginx 缓冲区持续堆积
– 集中在某几个 worker 的SSL_do_handshake() failed—— ssl_session_cache 配置过大或命中率低,TLS 握手反复申请堆内存
– WebSocket 长连接场景下,ss -nt显示大量 ESTABLISHED 连接的Send-Q堆积 —— 后端响应慢或客户端消费能力不足,导致 Nginx 缓冲区内存暴涨
排除配置假象,重点收缩高风险项
很多“OOM”其实是配置放大了内存需求,而非代码泄漏:
-
ssl_session_cache shared:SSL:XXm:启动时就预分配整块内存,容器或小内存机器极易失败。按公式算:
QPS(新建 HTTPS 连接数) × timeout(秒) × 0.5KB,例如 800 QPS × 600s ≈ 240MB → 设shared:SSL:256m即可 -
proxy_buffering on:保持开启,但收紧尺寸:
proxy_buffer_size 8k、proxy_buffers 16 8k(共 128KB),避免全局设大值 -
upstream keepalive:必须配
keepalive 200;+proxy_http_version 1.1;+proxy_set_header Connection '';,否则每请求建新连接,socket 缓冲 + proxy buffer 双重吃内存
不复杂但容易忽略。


















