引言
网络 IO 框架,最大的开销往往并不是在业务处理的环节,而是在高负载高并发下的 byte[] 对象申请或者 ByteBuffer 申请。很多时候,这些对象只是短暂地用于承载从通道读取数据或者向通道写出数据。在完成这个职能后,这些对象不再使用被 GC 回收。
当系统的负载很高时,频繁的申请 byte[] 或 ByteBuffer 对象,对内存的压力是很大的,很容易在短时间内消耗大量的内存。此时就只能等待 GC 将内存回收后系统才能继续运作。从 JVM 的监控表现来看,就是 GC 执行得十分频繁,耗时也多。表现在应用性能上,就是会出现时延毛刺和吞吐降低等情况。
Netty 4 相比于 Netty 3 有一个很大改变在于实现了自行管理的内存池,内存的申请和释放都在内存池上进行,就不再将这部分压力转嫁给 GC,带来了更平滑的吞吐和时延表现。
如何管理一块连续内存
内存池,其基本作用就是提供内存的申请和释放。而为了在这个过程中减少 GC,申请和释放的空间必然是其本身持有,不是在申请和释放的过程中再次向 JVM 申请。因此内存池本身一定是持有一大块可以用于分配的内存。对于 socket 通道而言,其数据读写的载体是 byte[] 对象。那么内存池的设计就可以简化为如何在一个 byte[] 上高效的申请和释放。
对于申请而言,其难度在于两个方面:
- 如何判断申请的长度在对应的 byte[]