ss -m 显示 TCP 内存远超进程 RSS,说明内核 TCP 缓冲区(rcv_buf/snd_buf)被大量占用,但用户态未显式关闭 socket 或关闭后因 FIN_WAIT2/TIME_WAIT 等状态未释放缓冲区。

ss -m 显示 TCP 内存远超进程 RSS,说明什么
这通常意味着内核 TCP 缓冲区(尤其是 rcv_buf / snd_buf)被大量占用,但对应 socket 没有被用户态进程显式 close,或 close 后因 FIN_WAIT2/TIME_WAIT 等状态未释放缓冲区。关键点在于:ss -m 统计的是内核网络栈的内存分配(sk->sk_wmem_alloc、sk->sk_rmem_alloc),而 top 的 RSS 只反映进程直接持有的用户态内存(如堆、栈、mmap 区),不包含内核为该进程维护的 socket 缓冲区内存。
确认是否真为 socket 泄漏而非正常积压
先排除流量突增或慢消费者导致的临时堆积:
- 用
ss -i state established | head -20查看 top 20 established 连接的rcv_queue和snd_queue—— 若持续 >1MB 且连接长时间存在,嫌疑大 - 检查
/proc/net/sockstat:重点关注sockets: used和TCP: inuse是否持续增长;mem字段即ss -m的总和来源 - 对比
ss -s输出中的memory行(单位页)与cat /proc/meminfo | grep Sockets—— 两者应基本一致,确认统计口径无误
定位具体泄漏 socket 所属进程
ss -tulpn 默认不显示内存详情,需配合 -m 和 -p(需 root):
- 运行
sudo ss -tulpnm | awk '$NF ~ /pid=/ {print $0}' | sort -k8,8nr | head -10,提取含pid=且按rmem/wmem降序的连接 - 若
-p失败(常见于容器或非 root 进程),改用sudo ss -tulnm获取 inode 号,再查ls -l /proc/*/fd/ | grep socket:匹配 inode,反向定位进程 - 注意:TIME_WAIT 连接不带
pid=,但其内存仍计入总量;可用ss -tan state time-wait | wc -l看数量,结合net.ipv4.tcp_fin_timeout判断是否异常堆积
常见陷阱与绕过方法
很多排查者卡在“看到高内存却找不到对应进程”,原因往往是:
- 应用使用了
SO_REUSEPORT,多个进程共享同一端口,ss -p可能只显示其中一个 pid - socket 被 fork 后子进程未关闭 fd(尤其 daemon 化场景),父进程退出后子进程继承但未管理该 socket
- Go 程序中
http.Transport配置不当(如MaxIdleConnsPerHost过大 + 空闲连接未复用),导致大量 ESTABLISHED 连接滞留 - Java 应用未正确关闭
HttpClient或Socket,GC 不回收底层 socket 资源(JDK 8u231+ 有改进,但旧版本仍常见)
真正棘手的是那些已 close() 但内核缓冲区未释放的情况——比如对端不读数据,导致本端 rcv_buf 满而无法滑动窗口,此时 socket 状态仍是 ESTABLISHED,内存就卡住了。








