ss -m 显示 TCP 内存占用巨大但进程 RSS 很小的 socket 泄漏排查

11次阅读

典型原因是应用层未及时读取或关闭连接导致内核缓冲区持续积;可通过ss -m查rmem/wmem、strace跟踪recv/read调用、tcpdump分析流量、检查setsockopt设置等交叉验证定位。

ss -m 显示 TCP 内存占用巨大但进程 RSS 很小的 socket 泄漏排查

ss -m 显示某个 socket 的 rmemwmem 达到几 MB 甚至上百 MB,而对应进程的 RSS(ps aux --sort=-rss/proc/PID/status 中的 RSS)却只有几百 KB,这通常不是内存统计口径差异的问题,而是典型的 **socket 接收/发送队列积压未消费**,即“socket 泄漏”的一种表现——更准确地说,是 **应用层未及时读取或关闭连接导致内核缓冲区持续堆积**。

确认是否为接收队列(rmem)积压

运行 ss -tulnmp | grep :PORT(替换 PORT),重点关注 Recv-Qrmem 字段:

  • Recv-Q 非零且持续增长 → 应用未调用 recv()/read() 消费数据
  • rmem 值远大于 sysctl net.core.rmem_max(如显示 2MB 但 rmem_max 是 212992)→ 内核已突破默认上限,说明该 socket 被长期持有且数据不断写入
  • 配合 cat /proc/PID/fd/ | wc -l 查看 fd 数是否异常增长,可辅助判断是否真有大量 socket 未 close

检查应用是否卡在阻塞 I/O 或逻辑死锁

很多服务(如 pythonsocket.recv()node.jsnet.SocketjavaInputstream.read())默认使用阻塞模式。一旦对端发来数据但业务逻辑未处理(比如反序列化失败、回调未注册、线程池耗尽),就会导致 recv 缓冲区越堆越多:

  • strace -p PID -e trace=recvfrom,read,close 观察是否有长时间无系统调用返回,或 recvfrom 返回 0(对端 FIN)但进程没 close
  • 检查日志中是否有反序列化异常、超时重试循环、空指针导致 handler 退出但 socket 未释放等逻辑缺陷
  • golang 程序要特别注意 goroutine 泄漏:启动了 go handleConn() 却因 channel 阻塞或 panic 未 recover,导致 conn 对象无法被 GC,底层 socket 一直存活

排查 SO_RCVBUF/SO_SNDBUF 手动设置过大或禁用自动调优

某些程序显式调用 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) 并设为极大值(如 16MB),又关闭了 TCP autotuning(net.ipv4.tcp_rmem 第三项被绕过),会导致单个 socket 占用内核内存失控:

  • bpftrace -e 'kprobe:tcp_setsockopt { printf("pid=%d, optname=%d\n", pid, arg2); }' | grep -E "(SO_RCVBUF|SO_SNDBUF)" 监控可疑 setsockopt 调用
  • 检查代码中是否硬编码了超大 buffer,并确认是否调用了 setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, ...)(需 CAP_NET_ADMIN)
  • 临时修复:改小 net.core.rmem_max(如 sysctl -w net.core.rmem_max=4194304),观察新连接是否仍堆积;长期应删掉非必要手动 setsockopt

验证是否由对端疯狂发包但本端无响应引起

即使你的程序逻辑正常,若上游客户端不遵守流控(如 udp 打洞后误用 TCP、iot 设备固件 bug),也会造成单边堆积:

  • tcpdump -i any port PORT -w debug.pcap 抓包,过滤出该 socket 的四元组,观察是否有大量重复 ACK、Zerowindow、或持续的 PSH+ACK 数据包涌入
  • 检查 /proc/net/snmpTcp: 行的 InErrsAttemptFails 是否突增,可能暗示连接建立失败后重传风暴
  • 在服务端加限速:用 tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 400ms 临时抑制流量,看 rmem 是否回落

这类问题本质是应用与内核协作失衡,核心不在“泄漏”而在“停滞”。定位关键在于交叉比对 ss -mstracetcpdump 和业务日志,找到那个“收得到但不敢/不能处理”的临界点。

text=ZqhQzanResources