Nginx 不执行 GC,504 是因后端 Java Full GC 导致 STW,使 Nginx 在 proxy_read_timeout 内收不到响应头而断连;需通过 Nginx error.log 定位超时时间窗口,再反向关联 JVM GC 日志、jstat 和 jstack 确认 STW;临时摘流、合理调优超时与健康检查,并改用 G1 GC 及排查内存泄漏。

看 Nginx 日志找“GC 时间窗口”
不要只扫 access.log,重点盯 error.log 中集中出现的超时信号:
-
大量
upstream timed out (110: Connection timed out) while reading response header from upstream:说明连接已建好、请求已发,但后端连响应头都没返回——这是 STW 最典型的外在表现 -
504 状态码在某几分钟内陡增,且无伴随
connection refused或no live upstreams:排除服务宕机或端口未监听,指向“活着但卡住” - 同一 upstream 地址失败高度集中(比如 10.0.1.23:8080 在 14:22–14:23 出现 47 次超时):立刻锁定该实例,暂停流量,准备查它的 JVM
查 Java 实例确认 GC 停顿
登录对应服务器,用时间戳对齐法验证是否真被 GC 卡住:
- 从 Nginx access.log 提取故障时刻,例如:
grep "504" /var/log/nginx/access.log | grep "14:22" | head -5 - 去该 Java 进程的 GC 日志里查同一秒级时间点:
grep "14:22:15" /var/log/app/gc.log,重点关注Pause、Full GC、G1 Evacuation Pause及其耗时(如Pause Young (G1 Evacuation Pause) 1287.3ms) - 用
jstat -gc <pid> 1000 5</pid>实时观察:若FGCT(Full GC 次数)突增,或GCT(总 GC 时间)在 1 秒内飙升 >500ms,基本坐实
抓线程快照判断是否 STW 中
GC 停顿时,几乎所有业务线程会卡在 RUNNABLE 状态但无实际堆栈推进:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 在故障窗口期(如 14:22:15±10 秒)执行:
jstack -l <pid> > thread_$(date +%H%M%S).dump</pid> - 打开 dump 文件,搜索
java.lang.Thread.State: RUNNABLE,若大量线程堆栈停留在Unsafe.park、VMThread或完全空白,就是 STW 正在发生 - 对比非故障时段的 dump,若此时线程普遍有清晰的业务方法调用链(如
UserService.getUser()),则进一步佐证是 GC 导致的假死
快速缓解与根治协同做
不能只等 GC 调优生效,要同步控制影响面:
立即学习“Java免费学习笔记(深入)”;
-
临时摘流:在 Nginx upstream 中对该节点加
down,或 K8s 中调高 readinessProbe 的failureThreshold,避免健康检查误判下线 -
延长超时但不盲目:把
proxy_read_timeout提到 120–300 秒,同时必须设proxy_buffering off和proxy_buffer_size 128k,否则缓冲区满会提前断连 -
让健康检查“感知 GC”:在
/actuator/health中加入逻辑,若最近 60 秒 Full GC 次数 ≥ 3,主动返回 503,触发 Nginx 自动剔除 -
根治聚焦 JVM 和代码:禁用 Parallel GC,改用
-XX:+UseG1GC -XX:MaxGCPauseMillis=200;用jmap -histo:live <pid></pid>对比对象增长,重点查byte[]、缓存类、未关闭的流

















