NIO比BIO复杂得多,NIO主要是多了Selector。Selector能监听多个Channel,当运行select()方法时,会循环检测是否有就绪事件的Channel。只需一条线程即可管理多个Channel,而且对Channel的读/写采用的都是非阻塞I/O。与BIO相比,NIO同时接入的Channel会更多、资源利用率会更高。因为BIO的一条线程只能对单个Channel进行阻塞读/写,处理完后才能继续接入并处理其他Channel,并发处理能力太弱.
零拷贝
1:Netty接收和发送ByteBuffer采用的都是堆外直接内存,使用堆外直接内存进行Socket的读/写,无须进行字节缓冲区的二次拷贝。
2:在网络传输中,一条消息很可能会被分割成多个数据包进行发送,只有当收到一个完整的数据包后,才能完成解码工作。Netty通过组合内存的方式把这些内存数据包逻辑组合到一块,而不是对每个数据块进行一次拷贝,这类似于数据库中的视图
3:当拷贝大文件时,频繁的内存拷贝操作会消耗大量的系统资源。Netty底层运用Java NIO的FileChannel.transfer()方法,该方法依赖操作系统实现零拷贝,可以直接将文件缓冲区的数据发送到目标Channel中,避免了传统的通过循环写方式导致的内存数据拷贝问题
Netty内存管理
(1)Netty在具体分配内存之前,会先获取本次内存分配的大小。具体的内存分配由PoolArena统一管理,先从线程本地缓存PoolThreadCache中获取,线程本地缓存采用固定长度队列缓存此线程之前用过的内存。
(2)若本地线程无缓存,则判断本次需要分配的内存大小,若小于512B,则先从PoolArena的tinySubpagePools缓存中获取;若大于或等于512B且小于8KB,则先从smallSubpagePools缓存中获取,上述两种情况缓存的对象都是PoolChunk分配的PoolSubpage;若大于或等于8KB或在SubpagePools缓存中分配失败,则从PoolChunkList中查找可分配的PoolChunk。
(3)若PoolChunkList分配失败,则创建新的PoolChunk(平衡二叉树),由PoolChunk完成具体的分配工作,最终分配成功后,加入对应的PoolChunkList中。若分配的是小于8KB的内存,则需要把从PoolChunk中分配的PoolSubpage加入PoolArena的SubpagePools中。