Netty并未重写Java原生Selector,而是通过JNI绕过空轮询Bug、用数组替换HashSet优化selectedKeys、封装Selector生命周期于EventLoop、增强事件注册与就绪处理语义。

Netty 并没有“重写” Java 原生 Selector,而是深度封装、绕过缺陷、替换关键数据结构,并在事件循环层进行重构。理解它,关键不是看它“替换了什么”,而是看它“为什么必须这么改”——源于 JDK NIO 在真实高并发场景下的几个硬伤。
空轮询 Bug 的绕过:不修内核,就换调用路径
Linux 下 JDK 的 EPollSelectorImpl 存在经典 Bug(JDK-6403933):select() 可能无故返回 0,导致线程空转、CPU 100%。原生 NIO 只能靠计数+重建 Selector 这种脆弱方式规避。
- Netty 不依赖 JDK 的
epoll_wait封装逻辑,而是通过 JNI 直接调用 Linuxepoll_create/epoll_ctl/epoll_wait系统调用; - 每次
epoll_wait返回后,Netty 强制检查内核就绪链表是否为空,为空则主动休眠,彻底阻断空轮询循环; - 这一层由
EpollEventLoop实现,仅在 Linux + epoll 场景启用,对 Windows(IOCP)或 macOS(kqueue)使用对应优化实现。
selectedKeys 性能瓶颈的替换:从哈希表到数组
JDK 原生 SelectorImpl 用 HashSet 存储就绪的 SelectionKey,但 HashSet.add() 在哈希冲突时最坏为 O(n),高并发下频繁触发,成为性能热点。
- Netty 通过反射获取 JDK Selector 内部的
selectedKeys字段,将其替换为自定义的SelectedSelectionKeySet; - 该集合底层是预分配数组(初始 1024 容量),
add()即简单赋值 + 自增索引,时间复杂度稳定为 O(1); - 扩容采用倍增策略,避免频繁内存分配;同时保证线程安全——仅由单个
NioEventLoop线程写入,无需锁。
Selector 生命周期与事件循环的解耦
原生 NIO 中,Selector 是用户直接操作的对象,生命周期、重建、注册迁移全由开发者手动维护,极易出错。
立即学习“Java免费学习笔记(深入)”;
- Netty 将
Selector完全封装进NioEventLoop,对外只暴露EventLoop接口; -
NioEventLoop持有私有Selector和unwrappedSelector(原始 JDK 实例),用于内部状态同步; - 当检测到空轮询阈值超限,或发生严重异常时,Netty 在
rebuildSelector()中自动完成:新建 Selector → 批量迁移有效 Channel → 关闭旧实例 → 切换引用,全程对上层 Handler 透明。
事件注册与就绪处理的语义增强
原生 SelectionKey 仅提供四种 OP 状态(ACCEPT/READ/WRITE/CONNECT),且就绪判断与业务处理耦合紧密。
- Netty 抽象出
AbstractNioChannel,将注册、事件触发、读写调度统一纳入ChannelPipeline流水线; - 就绪事件不再直接回调用户代码,而是封装为
Runnable提交到NioEventLoop任务队列,确保 I/O 与业务逻辑严格串行; - 支持更细粒度的就绪控制,例如可配置是否自动读(
autoRead)、是否延迟注册(registerLater),避免事件风暴。



















