跳到主要内容

Linux/Java 生产排查台账:Java 服务没挂,接口却报 Too many open files,怎么查?

01 前置理论与核心排查思路​

1. Too many open files 是什么问题?​

Linux 用文件描述符(FD)管理打开的文件、网络连接等资源。每个进程都有 FD 数量上限。

Java 程序建立连接后没有关闭,FD 会不断增加。达到上限后,新连接创建失败,日志可能出现:

java.io.IOException: Too many open files

2. FD 的软限制和硬限制​

执行:

grep -i 'open files' /proc/12345/limits

例如:

Max open files 256 1048576 files
  • 256:软限制。 当前进程实际生效的 FD 上限。
  • 1048576:硬限制。 软限制允许提高到的上限,提升权限受运行用户和系统限制约束。

/proc/12345/fd 记录进程当前打开的 FD。数量接近软限制时,继续用 lsof 查占用来源。

3. 通用排查路线​

找 Java 进程 → 查 FD 占用 → 查进程上限 → 查异常连接 → 临时止损 → 验证业务 → 交研发处理根因。


02 故障排查与恢复 SOP​

步骤一:查看报错,找到 Java 进程​

先看业务日志,确认是否出现 Too many open files。日志路径根据服务实际部署方式确定。

tail app.log

找到 Java 进程:

jps -l

例如:

12345 app.jar

下文的 12345 是示例 PID,执行时替换成实际 PID。没有 jps 可以使用 ps -ef | grep '[j]ava'。

步骤二:看 FD 占了多少​

ls /proc/12345/fd | wc -l

假设返回:

256

进程当前打开了 256 个 FD。

步骤三:查看 FD 上限​

grep -i 'open files' /proc/12345/limits

假设返回:

Max open files 256 1048576 files

FD 已经占满。接下来用 lsof 查具体占用。

步骤四:用 lsof 定位占用来源​

查看 Java 进程打开的 TCP 连接:

lsof -p 12345 | grep -i tcp | head

示例输出:

java 12345 app 5u IPv6 ... TCP *:80 (LISTEN)
java 12345 app 6u IPv6 ... TCP localhost:80->localhost:41198 (CLOSE_WAIT)
java 12345 app 7u IPv6 ... TCP localhost:80->localhost:41204 (CLOSE_WAIT)
java 12345 app 9u IPv6 ... TCP localhost:80->localhost:41212 (CLOSE_WAIT)

如果存在大量长时间堆积的 CLOSE_WAIT,重点排查程序是否没有关闭 Socket。

CLOSE_WAIT 表示对端已关闭连接,本地程序还没关闭 Socket。大量连接持续停留在这个状态,重点排查 Socket 是否遗漏关闭。

TCP 连接不多时,直接执行 lsof -p 12345,检查日志文件、管道等其他占用。

步骤五:先保留关键证据​

调整上限前,保留报错日志、FD 数量与上限、异常连接明细。

例如:

grep -i 'open files' /proc/12345/limits > fd-limits.txt
ls /proc/12345/fd | wc -l > fd-count.txt
lsof -p 12345 | grep -i tcp > tcp.txt

日志保存报错片段即可。交给研发前处理敏感信息。

步骤六:临时调高进程 FD 上限​

FD 到达软限制、硬限制还有余量时,可以临时提高该进程的软限制,恢复新连接处理能力。

假设软限制是 256、硬限制是 1048576,可以执行:

prlimit -p 12345 -n 1024:1048576

再确认:

grep -i 'open files' /proc/12345/limits

执行后软限制应变为 1024。硬限制按现场查询结果填写;操作其他用户的进程需要相应权限。

prlimit 只调整目标进程的限制。已泄漏的连接不会自动释放。

提高上限后仍无法恢复时,评估重启服务或切换流量。

步骤七:验证接口恢复,再交给研发​

对实际业务接口重新发起请求,例如:

curl localhost

接口返回正常后,再看错误日志和 FD 数量是否继续增长。

交接研发时明确三件事:

  1. Java 进程 FD 达到软限制,日志报 Too many open files。
  2. 大量 TCP 连接长期处于 CLOSE_WAIT,怀疑连接未释放。
  3. 已临时提高 FD 上限并恢复服务,需要研发检查连接关闭逻辑,防止再次耗尽。

临时调高上限后,研发仍需修复未关闭的连接。


03 典型生产故障案例​

案例一:大量 CLOSE_WAIT 堆积,接口开始报错​

故障现象

Java 进程还在,HTTP 接口大量失败,日志持续出现 Too many open files。

排查过程

jps -l
ls /proc/12345/fd | wc -l
grep -i 'open files' /proc/12345/limits
lsof -p 12345 | grep -i tcp | head

发现 FD 占用达到软限制,lsof 中存在大量持续不消失的 CLOSE_WAIT。

原因与处理

连接已经被对端关闭,但 Java 程序没有及时释放对应的 Socket,FD 持续堆积。保留证据后评估临时调高上限,验证接口恢复,并把连接关闭问题交给研发。

处理:先恢复接口,再由研发修复 Socket 关闭逻辑。

案例二:FD 用满,但 TCP 连接并不多​

故障现象

服务同样出现 Too many open files,但 lsof 没有发现大量 TCP 连接。

排查方向

ls /proc/12345/fd | wc -l
lsof -p 12345 | head -n 30

继续检查进程打开的日志、普通文件、管道等资源;必要时统计各类 FD 的数量。

原因与处理

可能是程序重复打开文件流后没有关闭,例如读取文件、生成报表或处理上传时遗漏资源释放。先按业务情况止损,再让研发定位具体资源创建与关闭逻辑。

处理:按 lsof 结果确定是文件流还是网络连接占用。

案例三:调高上限后恢复,过一段时间又报错​

故障现象

把软限制从 256 提高到 1024 后接口暂时恢复,但几个小时后又出现同样的报错。

排查方向

对比不同时间的 FD 数量、CLOSE_WAIT 数量和错误日志,确认占用是否持续增长。

原因与处理

提高 FD 上限只是扩大了可用名额,没有修复泄漏。应由研发排查 Socket、HTTP 客户端或连接池关闭逻辑,同时根据增长速度安排风险处置。

处理:持续观察 FD 增长,安排研发修复泄漏。


04 生产排障命令速查表​

以下 PID、日志路径和端口均为示例,按现场替换。

排查目标命令看什么
查看报错日志tail app.logToo many open files
找 Java 进程jps -lPID、JAR 名称
JDK 工具不可用ps -ef | grep '[j]ava'对应 Java 进程
当前 FD 数量ls /proc/12345/fd | wc -l是否持续增长、接近上限
FD 软硬限制grep -i 'open files' /proc/12345/limits第 1 个值是软限制,第 2 个是硬限制
进程所有 FDlsof -p 12345文件、网络连接、管道等
TCP 连接lsof -p 12345 | grep -i tcp | head大量异常连接
CLOSE_WAIT 数量lsof -p 12345 | grep -c CLOSE_WAIT是否持续堆积
查看进程当前限制prlimit -p 12345 -n当前 NOFILE 软硬限制
临时提高软限制prlimit -p 12345 -n 1024:1048576按实际硬限制调整
验证 HTTP 接口curl localhost返回是否正常

最快排查路线:jps 找进程 → /proc/PID/fd 查占用 → /proc/PID/limits 查上限 → lsof 查原因。


05 面试应答参考话术​

问题:Java 服务没退出,接口却报 Too many open files,你会怎么处理?

先确认业务进程还在运行,并查看错误日志。通过 jps -l 找到 PID,用 /proc/PID/fd 统计当前文件描述符数量,再从 /proc/PID/limits 查看软硬限制。

如果 FD 接近软限制,我会用 lsof -p PID 看具体被什么占用。发现大量持续堆积的 CLOSE_WAIT,就重点怀疑程序没有正确关闭网络连接。

线上先保留日志和连接证据。如果硬限制允许,可以临时提高进程 FD 软限制,验证接口是否恢复;无法恢复时再按应急流程处理。最后把证据交给研发修复连接泄漏,并持续观察 FD 是否增长。


06 面试高频追问与应答预案​

Q1:文件描述符是什么?​

进程访问文件、网络 Socket、管道等资源时,Linux 使用 FD 来标识。FD 数量有进程级限制,耗尽后新资源可能打开失败。

Q2:软限制和硬限制有什么区别?​

软限制是当前生效的上限;硬限制是普通进程能把软限制提高到的边界。修改硬限制或提高其他进程的限制可能需要额外权限。

Q3:为什么 Java 进程还在,接口却不能访问?​

FD 耗尽后,Java 无法接收新连接或打开新文件,接口会报错,但进程仍然可以运行。

Q4:CLOSE_WAIT 是什么意思?​

对端已经关闭连接,本地应用还没关闭 Socket。短暂出现正常;大量连接持续堆积,通常要检查应用是否及时释放连接。

Q5:看到大量 CLOSE_WAIT,能直接认定代码泄漏吗?​

结合 FD 增长速度和错误日志判断。大量 CLOSE_WAIT 持续不消失,再排查连接关闭逻辑。

Q6:为什么 lsof 能查 TCP 连接?​

Socket 会占用 FD,lsof 可以直接列出进程的 TCP 连接。

Q7:直接执行 ulimit -n 65535 能修复正在运行的 Java 服务吗?​

ulimit 调整当前 Shell 及随后启动的子进程。已经运行的 Java 进程使用 prlimit 调整。

Q8:prlimit 提高上限后,已经泄漏的连接会自动关闭吗?​

不会。它增加可用 FD 数量,泄漏的连接仍然占用原来的 FD。

Q9:prlimit 调整后,服务重启还会保留吗?​

通常不会。新进程从 Shell、systemd 或容器运行环境继承限制,长期生效要修改启动配置。

Q10:什么时候不建议直接调高 FD 上限?​

进程已不稳定、资源持续快速增长或主机整体资源紧张时,调高上限可能放大影响。应先评估业务与主机资源,再决定是否切流或重启。

Q11:线上恢复后怎么判断根因是否解决?​

在相同请求量下,检查 FD 是否稳定、CLOSE_WAIT 是否消退、接口是否继续报错。

Q12:运维最终要交给研发什么?​

故障时间、Java PID、错误日志、FD 占用与限制、lsof 异常连接信息,以及临时止损操作和业务验证结果。


07 文件描述符一键检查脚本(只读)​

适合已经找到 Java PID,需要快速确认 FD 是否接近上限、CLOSE_WAIT 是否堆积的场景。

保存为 check_java_fd.sh:

#!/usr/bin/env bash
# 用法:bash check_java_fd.sh PID
set -eu

pid=${1:-}
if [[ ! "$pid" =~ ^[0-9]+$ ]] || [[ ! -d "/proc/$pid/fd" ]]; then
echo '用法: bash check_java_fd.sh 有效PID' >&2
exit 1
fi

limit=$(awk '$1=="Max" && $2=="open" && $3=="files" {print $4, $5}' "/proc/$pid/limits")
read -r soft hard <<< "$limit"
count=$(ls -1 "/proc/$pid/fd" 2>/dev/null | wc -l)

printf 'PID: %s\nFD使用: %s\n软限制: %s\n硬限制: %s\n' "$pid" "$count" "$soft" "$hard"

if [[ "$soft" =~ ^[0-9]+$ ]] && (( soft > 0 )); then
awk -v n="$count" -v max="$soft" 'BEGIN {printf "使用率: %.1f%%\n", n/max*100}'
fi

if command -v lsof >/dev/null 2>&1; then
close_wait=$(lsof -nP -a -p "$pid" -iTCP 2>/dev/null | grep -c CLOSE_WAIT || true)
printf 'CLOSE_WAIT: %s\n' "$close_wait"
else
echo '未安装 lsof,跳过连接状态检查'
fi

使用示例:

bash check_java_fd.sh 12345

输出示例:

PID: 12345
FD使用: 256
软限制: 256
硬限制: 1048576
使用率: 100.0%
CLOSE_WAIT: 245

脚本只读取 /proc 和 lsof 信息,不会调整限制、关闭连接或重启服务。需以能查看目标进程信息的用户执行。


08 现场注意事项与故障复现资料​

  • 查看 FD 上限、实际占用以及增长速度。
  • 结合错误日志和 CLOSE_WAIT 数量判断连接是否堆积。
  • 提高上限后继续观察 FD 增长,及时安排研发修复。
  • 记录临时处理措施和后续负责人。

测试环境复现思路​

在隔离的 Linux 测试机上,使用一个故意不关闭 Socket 的 Java HTTP 服务,给该服务单独设置较低 FD 上限,再用并发请求触发 Too many open files。

复现时应观察四项结果:

  1. 接口起初正常,持续请求后开始报错。
  2. 日志真实出现 Too many open files。
  3. 进程 FD 占用达到软限制,存在大量持续堆积的 CLOSE_WAIT。
  4. 临时提高软限制后,接口可恢复,但 FD 仍可能继续增长。

配套故障复现 JAR 作为独立附件提供,仅在隔离测试机使用。

完整内容已隐藏

请联系作者或在线获取阅读密码。