
2.5 零拷贝
序列化主要与传输数据格式有关,不管是Kryo还是Protobuf,它们都能对数据内容进行压缩,并能完整地恢复。零拷贝是Netty的一个特性,主要发生在操作数据上,无须将数据Buffer从一个内存区域拷贝到另一个内存区域,少一次拷贝,CPU效率就会提升。Netty的零拷贝主要应用在以下3种场景中。
(1)Netty接收和发送ByteBuffer采用的都是堆外直接内存,使用堆外直接内存进行Socket的读/写,无须进行字节缓冲区的二次拷贝。如果使用传统的堆内存进行Socket的读/写,则JVM会将堆内存Buffer数据拷贝到堆外直接内存中,然后才写入Socket中。与堆外直接内存相比,使用传统的堆内存,在消息的发送过程中多了一次缓冲区的内存拷贝。
(2)在网络传输中,一条消息很可能会被分割成多个数据包进行发送,只有当收到一个完整的数据包后,才能完成解码工作。Netty通过组合内存的方式把这些内存数据包逻辑组合到一块,而不是对每个数据块进行一次拷贝,这类似于数据库中的视图。CompositeByteBuf是Netty在此零拷贝方案中的组合Buffer,在第4章节会对它进行详细剖析。
(3)传统拷贝文件的方法需要先把文件采用FileInputStream文件输入流读取到一个临时的byte[]数组中,然后通过FileOutputStream文件输出流,把临时的byte[]数据内容写入目的文件中。当拷贝大文件时,频繁的内存拷贝操作会消耗大量的系统资源。Netty底层运用Java NIO的FileChannel.transfer()方法,该方法依赖操作系统实现零拷贝,可以直接将文件缓冲区的数据发送到目标Channel中,避免了传统的通过循环写方式导致的内存数据拷贝问题。
本节内容偏少,且理论性较强。零拷贝机制在做上层应用时几乎不会接触,但在面试时,很有可能被问到,因此一定要深入理解这3种重要的零拷贝场景。如果目前难以理解,则可暂时跳过,在仔细看完源码剖析部分内容后,再来仔细分析。