应先分层验证系统级(file-nr第二列)与进程级(/proc/pid/limits)实际使用量,再定位泄漏源而非盲目调参;systemd服务需配LimitNOFILE,容器须在运行时指定ulimit,且file-max须≤nr_open以防静默失败。

遇到“Too many open files”或连接建立失败、超时等现象,不能直接认定是句柄限制问题,得先确认是不是真满了、谁在吃、为什么没释放——否则调参只是把崩溃推迟几小时。
看系统到底满没满
别只盯着报错,要分两层验证:
- 系统级总量:执行 cat /proc/sys/fs/file-nr,输出三列,第二列才是「当前活跃占用」的句柄数(已分配且正在用),它接近第三列(file-max)才危险;
- 进程级限额:查目标进程真实限制,比如 nginx,运行 cat /proc/$(pgrep nginx | head -n1)/limits | grep "Max open files",重点看 Soft Limit 是否远低于实际使用量。
找哪个进程在疯狂吃句柄
全局排查最有效的方法是:
- 执行 lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | head -10,列出打开句柄最多的前10个 PID;
- 再针对高 PID 查类型分布:lsof -p
| awk '{print $8}' | sort | uniq -c | sort -nr | head -10 ,看是不是大量 IPv4(连接)、REG(文件)、pipe 或 anon_inode(NIO 常见泄漏点); - 对 Java 应用,可加采样比对:lsof -p $(pgrep -f 'java.*YourApp') | wc -l,每分钟执行一次,观察是否持续上涨。
确认配置改了到底有没有生效
很多改了 limits.conf 却不生效,是因为启动方式不同:
- 交互式登录用户:改完 /etc/security/limits.conf 后必须新开 login shell(不是 su 或子 shell),且确认 /etc/pam.d/common-session 包含 session required pam_limits.so;
- systemd 服务(如 nginx、redis、Java 应用):limits.conf 完全无效,必须在 service 文件里加 LimitNOFILE=65536,然后 systemctl daemon-reload && systemctl restart xxx;
- 容器环境(Docker/K8s):宿主机改没用,需在 docker run --ulimit nofile=65536:65536 或 Pod 的 securityContext.fdsLimit 中显式设置。
排除泄漏,不止靠调大
如果句柄数随时间稳定上升,说明存在泄漏:
- 检查代码:Java 要确保所有 InputStream、Socket、Connection 都在 try-with-resources 或 finally 中 close;
- 检查日志滚动:Logback 或 Log4j 配置不当(如未启用 prudent mode 或文件句柄未释放)会累积 REG 类型句柄;
- 检查网络状态:用 netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 看是否有大量 CLOSE_WAIT 或 TIME_WAIT 不释放,可能对应连接池未正确回收。

















