这是典型的内核socket缓冲区泄漏,ss -m显示的skmem_rmem/skmem_wmem属内核sk_buff内存,不计入进程RSS;需通过ino定位残留socket,结合状态(如CLOSE_WAIT)、重传、TCP参数等综合分析。

ss -m 显示 skmem_rmem/skmem_wmem 巨大但 ps/top RSS 很低
这是典型的内核 socket 缓冲区泄漏,而非用户态内存泄漏。ss -m 中的 skmem_rmem 和 skmem_wmem 是内核为 socket 分配的接收/发送队列内存(属于 slab 或 page 级别),不计入进程的 RSS(只统计用户态匿名页和文件映射页)。进程可能已退出,但 socket 仍处于 CLOSE_WAIT、FIN_WAIT2 或未正确 close() 的半关闭状态,导致内核无法释放缓冲区。
-
ss -m显示的内存是 per-socket 的sk->sk_rmem_alloc/sk->sk_wmem_alloc,单位是字节,可远超单个 socket 的rmem_max/wmem_max(尤其在有大量未确认数据或排队 sk_buff 时) - 进程
RSS低说明它没在用户态 malloc 大量内存,但可能反复send()后不recv(),或shutdown(SHUT_WR)后不读完对端 FIN,造成接收队列持续堆积 - 注意
ss -m输出中ino字段——这是 socket 对应的 inode 号,可用于关联/proc/*/fd/和/proc/*/net/tcp
用 ino 定位残留 socket 所属进程(含已退出但未 fully close 的)
即使进程已 exit,只要 socket 还持有引用(如被子进程继承、或存在 file descriptor 引用但未 close),其 inode 就仍存在于 /proc/net/tcp 或 /proc/net/tcp6 中。关键不是找“活着的进程”,而是找“谁最后打开过这个 socket”。
- 运行
ss -tulnmp | grep 'skmem_rmem.*[0-9]{7,}'提取高内存 socket 的ino值(例如ino:123456789) - 用
find /proc/[0-9]*/fd/ -lname "socket:[123456789]" 2>/dev/null查找所有指向该 inode 的 fd 符号链接;若无结果,说明进程已退出但 socket 未释放(常见于孤儿连接或内核 refcount 泄漏) - 若有结果,进入对应
/proc/PID/目录,检查comm、cmdline和stack(需 root):cat /proc/PID/stack可看线程是否卡在tcp_recvmsg、sock_sendmsg或epoll_wait等调用点
检查 netstat/ss 输出中的状态与重传标志
仅看内存值不够,必须结合 socket 状态判断泄漏类型。重点关注 CLOSE_WAIT(对端已 FIN,本端不 close)、ESTABLISHED 但 retrans 高、或 FIN_WAIT2 长时间不超时。
-
ss -tunp state close-wait | wc -l:CLOSE_WAIT 过多通常意味着应用层未调用close(),尤其在 HTTP keep-alive 或数据库连接池未正确归还连接时 -
ss -i(带 TCP info)可显示retrans、qsize、rcv_space;若retrans > 0且qsize持续增长,可能是对端不可达或应用层写阻塞未处理 -
netstat -s | grep -A5 "Tcp:"查看全局TcpExtTCPAbortOnMemory或TcpExtTCPMemoryPressures计数,若频繁触发,说明内核已因 socket 内存耗尽开始 abort 连接
验证是否由 SO_RCVBUF/SO_SNDBUF 设置不当引发
显式调大 socket 缓冲区(如 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)))会直接放大 skmem_rmem 占用,但若应用未及时 recv(),缓冲区就变成“内存黑洞”。Linux 4.6+ 默认启用自动调优(net.ipv4.tcp_rmem 第三项为 max),但手动设置会禁用 auto-tuning。
- 检查
/proc/sys/net/ipv4/tcp_rmem和/proc/sys/net/ipv4/tcp_wmem,若第二/三字段过大(如4096 65536 16777216),且应用又未做流控,极易堆积 - 用
ss -i观察单个 socket 的rcv_ssthresh和rcv_space:若rcv_space接近tcp_rmem[2]且rcv_ssthresh很小,说明接收窗口已关死,但数据还在进 - 临时缓解可调低全局上限:
echo "4096 65536 4194304" > /proc/sys/net/ipv4/tcp_rmem,但根本解法是修复应用层 read loop 或增加超时
真正难定位的是那些 ino 找不到对应进程、ss -m 内存缓慢上涨、且 netstat -s 中 TcpExtTCPAbortOnMemory 不增不减的情况——这往往指向内核模块 bug、eBPF 程序意外 hold socket ref、或 cgroup v1 的 socket 内存 accounting 残留。这时候得用 bpftrace hook tcp_close 和 sk_stream_kill_queues 看谁没被调用。










