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

Nginx 本身不执行 GC,所谓“Nginx 因 Full GC 报 504”其实是后端 Java 应用在长时间 STW(Stop-The-World)期间无法写入响应头,导致 Nginx 在 `proxy_read_timeout` 内收不到任何数据而主动断连。排查关键在于**用 Nginx 日志做时间锚点,反向关联 Java 进程的真实 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实时观察:若1000 5 FGCT(Full GC 次数)突增,或GCT(总 GC 时间)在 1 秒内飙升 >500ms,基本坐实
抓线程快照判断是否 STW 中
GC 停顿时,几乎所有业务线程会卡在 RUNNABLE 状态但无实际堆栈推进:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 在故障窗口期(如 14:22:15±10 秒)执行:
jstack -l> thread_$(date +%H%M%S).dump - 打开 dump 文件,搜索
java.lang.Thread.State: RUNNABLE,若大量线程堆栈停留在Unsafe.park、VMThread或完全空白,就是 STW 正在发生 - 对比非故障时段的 dump,若此时线程普遍有清晰的业务方法调用链(如
UserService.getUser()),则进一步佐证是 GC 导致的假死
快速缓解与根治协同做
不能只等 GC 调优生效,要同步控制影响面:
-
临时摘流:在 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对比对象增长,重点查byte[]、缓存类、未关闭的流
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










