Windows 的 IOCP 是内核级真异步,Linux 传统 AIO 长期依赖 epoll 模拟,直到 io_uring 出现才开始真正追赶;JDK 虽统一 API,但底层行为差异导致性能断层。

Java NIO.2(AIO)在 Linux 和 Windows 上的性能断层,根本不在 Java 层,而在于底层操作系统对“异步 I/O”的原生支持能力存在代际差距:Windows 的 IOCP 是内核级真异步,Linux 传统 AIO 长期是模拟层,直到 io_uring 出现才开始真正追赶。
IOCP 是闭环式真异步,内核全程接管数据搬运
Windows 下 AsynchronousSocketChannel 等通道最终由 IOCP(I/O Completion Port) 驱动:
- 应用调用 WSARecv/WSASend + OVERLAPPED 后立即返回,不参与任何拷贝
- 内核在网卡 DMA 完成、协议栈处理完毕、数据从内核缓冲区搬入用户内存后,才投递完成包
- 线程通过 GetQueuedCompletionStatus 拿结果,无锁、无竞争、无状态同步开销
- 单个完成端口可被数十线程共享,连接数与线程数彻底解耦,吞吐随 CPU 核心线性扩展
Linux 传统 AIO 长期依赖 epoll 模拟,本质仍是同步非阻塞
Java 在旧版 Linux 上的 AsynchronousChannelProvider 实际走的是“伪 AIO”路径:
- epoll 本身只是事件通知机制,不提供异步读写能力
- 它监听 socket 可读/可写,但后续 recv()/send() 仍需用户线程同步执行,数据拷贝仍在用户态发生
- 为避免丢数据,常需边缘触发(ET)+ 循环读取,易引发饥饿或空转
- 多个线程无法安全共用一个 epoll fd,需加锁或绑定线程,失去负载均衡优势
io_uring 是 Linux 真 AIO 的转折点,但生态和 JVM 适配尚未成熟
Linux 5.1+ 的 io_uring 才首次提供了接近 IOCP 能力的异步接口:
立即学习“Java免费学习笔记(深入)”;
- 支持提交请求即返回,内核后台执行,完成时轮询或事件通知
- 有零拷贝优化(如 IORING_OP_SENDFILE)、批处理、无锁提交队列
- 但 JDK 目前未默认启用 io_uring 后端,多数发行版仍 fallback 到 epoll 模拟
- glibc 封装不完善,JVM 需深度集成新系统调用,调试与监控工具链也滞后
JVM 抽象层掩盖差异,却无法抹平底层鸿沟
AsynchronousChannelProvider 的 SPI 架构虽统一了 API,但实际行为天差地别:
- 在 Windows 上,一次 write() 调用背后是内核闭环调度 + DMA + 中断驱动
- 在旧 Linux 上,同一 write() 可能被降级为:注册 epoll → 等待就绪 → 用户线程调用 send() → 等待拷贝完成
- 这种路径长度、上下文切换次数、CPU 占用模式的差异,直接反映在高并发低延迟场景下的 P99 延迟与吞吐稳定性上



















