linux socket大规模并发时内存开销主要来自内核:每条established连接约耗4.2 kb,10万连接达400 mb;time_wait单个约0.7–1.2 kb,3万个约25–35 mb,并加剧slab碎片和conntrack压力。

Linux socket 处理大规模并发连接时,内存开销主要来自内核为每个连接分配的结构体、缓冲区和状态跟踪资源,而非应用层代码直接申请的内存。真正吃内存的是内核——尤其是 ESTABLISHED 连接和大量 TIME_WAIT 状态连接。
每条 ESTABLISHED 连接约占用 3.5–6 KB 内核内存
这包括:
- struct sock 对象:核心网络套接字结构,含 TCP 控制块(tcp_sock)、接收/发送队列指针等,典型大小约 1.8–2.5 KB(取决于内核版本和编译选项);
- 接收缓冲区(sk_receive_queue):默认 net.ipv4.tcp_rmem(4K–128K–6M),但实际按需分配,空闲连接通常只占几页(8–16 KB);
- 发送缓冲区(sk_write_queue):类似,空闲连接几乎不占额外页;
- 关联的 sk_buff(skb)缓存、路由缓存、连接跟踪(nf_conntrack)条目(若启用防火墙/NAT)。
实测中,一个空载的 ESTABLISHED 连接在 5.10+ 内核上平均消耗约 4.2 KB 内核内存。10 万连接 ≈ 占用 400 MB 内核内存——这已接近普通 8 GB 服务器的内核内存压力阈值。
TIME_WAIT 连接更“轻量”,但数量级大时仍不可忽视
TIME_WAIT 状态连接不维护完整 TCP 控制块,仅保留 minimal sock 结构 + 四元组哈希索引,单个约 0.7–1.2 KB。但问题在于:
- 它受 net.ipv4.ip_local_port_range(默认 32768–65535,仅 32768 个可用端口)限制,高并发短连接服务极易打满;
- 3 万个 TIME_WAIT 连接 ≈ 消耗 25–35 MB 内核内存——看似不多,但叠加 ESTABLISHED、socket 缓冲区、slab 分配碎片后,会显著抬升 Slab 内存占用(可通过 cat /proc/meminfo | grep Sla 查看);
- 大量 TIME_WAIT 会挤占 net.netfilter.nf_conntrack_max(若开启 conntrack),导致新连接被丢弃。
内核内存管理机制直接影响实际开销
Linux 使用 SLUB 分配器管理 sock 对象等小对象,其行为关键点有:
- 每个 CPU 有自己的 slab 缓存(per-CPU cache),减少锁竞争,但会略微增加内存碎片;
- sock 对象从 tcp_sock_cache slab 中分配,复用率高可降低分配开销;
- NUMA 架构下,若进程绑定在某 node,但 sock 对象分配到远端 node 的内存,会带来延迟与带宽损耗——建议用 numactl --cpunodebind=0 --membind=0 启动服务进程;
- 内核不会立即释放刚关闭的 sock,而是进入 slab 的 partial list 或 full list,直到内存压力触发回收。
真正压垮内存的往往是“隐性开销”
除连接本身外,以下因素常被低估:
- 文件描述符表:每个 socket 占一个 fd,fd 数量上限(fs.file-max)虽可调,但每进程 fd 表本身也占内存(约 8 字节/fd × 进程数 × 平均 fd 数);
- epoll 实例:每个 epoll fd 维护红黑树 + 就绪链表,10 万监听 fd 的 epoll 实例自身约占 20–30 MB;
- page cache 压力:若应用频繁 sendfile 或 mmap 文件,会与 socket 缓冲区争抢 page cache,触发全局内存回收,间接影响网络吞吐;
- netfilter 规则复杂度:iptables/nftables 规则越多,每个包匹配耗时越长,CPU 时间变相转化为内存等待时间(如 recv() 阻塞延长)。











