发布于2026-08-15 阅读(0)
扫一扫,手机访问
需要立刻提高警觉的 FD 行,通常有这么几类:一是持续递增的数字,比如1024r、1025r;二是 TYPE=REG 且 NAME 中带有(deleted);三是 TYPE=anon_inode/pipe,且数量和线程数明显强相关;四是 TYPE=sock、STATE=ESTABLISHED,但业务逻辑里又找不到对应连接;五是 TYPE=DIR 指向临时目录;六是 NAME 为空,或者直接显示为 socket:[xxx]。

正常进程的 FD 列集中在 0u、1w、2w、cwd、mem、DEL 或少量数字(如 15u)。一旦发现以下模式,基本可判定存在泄漏:
FD 列持续出现大量递增数字(如 1024r、1025r、1026w…),且 NAME 指向同一类路径(如 /tmp/xxx.log、/var/log/app/*.log)TYPE=REG + NAME 含 (deleted),且数量随时间增长 → 文件已被 unlink 但句柄未关,磁盘空间不释放TYPE=anon_inode 或 pipe 数量与线程数强相关(如每多一个线程就多 2 个 pipe + 1 个 eventpoll)→ 常见于未退出的 Looper、epoll 或异步 I/O 初始化未清理TYPE=sock 且 STATE=ESTABLISHED 但无业务逻辑对应 → 可能是 HTTP 客户端连接池未 close,或 socket 未 shutdownTYPE=DIR 指向临时目录(如 /tmp/upload_abc)→ 检查代码中是否用 Files.list()、new File(dir).listFiles() 后没关闭 Stream 或 DirectoryStreamNAME 为空或显示 socket:[1234567] → 这类句柄需结合 /proc/PID/fd/ 下实际符号链接反查,用 ls -l /proc/PID/fd/ | grep socket这是最易踩坑的点:lsof 默认只显示当前打开的句柄,但某些泄漏是“瞬时创建 + 长期持有”,而 lsof 快照无法捕捉趋势。更关键的是:lsof 本身可能因 DNS 解析卡住,导致输出失真或漏项。
-n(禁用 DNS 解析)和 -P(禁用端口名解析),否则排查过程可能引入干扰lsof -p PID | wc -l 只是快照,无法判断趋势。应执行 watch -n 5 'lsof -n -P -p PID | wc -l',观察 30 秒内是否稳定或缓慢爬升(>5 行/秒即异常)lsof -n -P -p PID 2>/dev/null | grep -E "^(ja va|python|node)" | head -20,确认新增句柄是否集中于某类文件(如全是 .jar、.pyc、socket)lsof -n -P -p PID | grep "access.log" 和 ls -la /var/log/nginx/access.log*,看是否还在写旧文件lsof 有时会过滤掉某些内核抽象资源(比如部分 anon_inode 或容器环境中的 fd),而 /proc/PID/fd/ 是内核直接暴露的符号链接视图,更真实、无过滤。
ls -l /proc/PID/fd/,你会看到类似 1024 -> socket:[1234567]、1025 -> anon_inode:[eventfd] 的条目readlink /proc/PID/fd/1024 获取原始类型;再配合 cat /proc/PID/status | grep -i fdsize 查看当前 fd 总数是否接近 ulimit -n 设置值socket:[*] 或 anon_inode:[*],且数量与线程数/连接数匹配,大概率是未调用 close() 或未触发资源自动释放(如 Ja va 中未用 try-with-resources)/proc/PID/fd/ 需要目标进程属主权限,否则会提示 Permission denied;此时必须用 sudo光看出现在哪类 fd 不够,得确认它是否真的来自你控制的代码路径。很多泄漏藏在标准库封装之下,比如 Files.list()、ZipInputStream、HTTP 客户端默认连接池、甚至日志框架的异步刷盘线程。
Files.list()、Files.walk()、ZipFile、SocketChannel.open() —— 它们都返回需显式关闭的资源,不能只靠 GCopen() 是否配对 close(),或是否用了 with;os.scandir() 同样需 close(),subprocess.Popen 的 stdout/stderr 也容易漏关fs.createReadStream()、http.request()、net.createConnection() 都需显式 .destroy() 或监听 close 事件NAME 列,而是表现为 TYPE=sock 或 TYPE=anon_inode,这时要回溯调用栈,看是否在循环中反复 new client 但没 closesocket:[1234567] 或 anon_inode:[eventpoll] 谁创建的、谁该负责关、为什么没关。这时候 lsof 和 /proc/PID/fd/ 只是入口,最终还得靠代码上下文和资源生命周期契约。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
4
5
6
7
8
9