引言
网络 IO 框架,最大的开销往往并不是在业务处理的环节,而是在高负载高并发下的 byte[] 对象申请或者 ByteBuffer 申请。很多时候,这些对象只是短暂地用于承载从通道读取数据或者向通道写出数据。在完成这个职能后,这些对象不再使用被 GC 回收。
当系统的负载很高时,频繁的申请 byte[] 或 ByteBuffer 对象,对内存的压力是很大的,很容易在短时间内消耗大量的内存。此时就只能等待 GC 将内存回收后系统才能继续运作。从 JVM 的监控表现来看,就是 GC 执行得十分频繁,耗时也多。表现在应用性能上,就是会出现时延毛刺和吞吐降低等情况。
Netty 4 相比于 Netty 3 有一个很大改变在于实现了自行管理的内存池,内存的申请和释放都在内存池上进行,就不再将这部分压力转嫁给 GC,带来了更平滑的吞吐和时延表现。
如何管理一块连续内存
内存池,其基本作用就是提供内存的申请和释放。而为了在这个过程中减少 GC,申请和释放的空间必然是其本身持有,不是在申请和释放的过程中再次向 JVM 申请。因此内存池本身一定是持有一大块可以用于分配的内存。对于 socket 通道而言,其数据读写的载体是 byte[] 对象。那么内存池的设计就可以简化为如何在一个 byte[] 上高效的申请和释放。
对于申请而言,其难度在于两个方面:
- 如何判断申请的长度在对应的 byte[] 存在足够的空闲空间。
- 如何寻找申请长度在 byte[] 上对应的空闲空间。
让我们先略过问题一,先关注到问题二。初始情况下,在一个“空白”的 byte[] 上申请空间是最容易的,因为此时只需要判断数组的长度和申请
本文探讨了网络IO框架中内存管理的重要性,特别是Netty 4引入的内存池机制,旨在减少GC压力并提高性能。通过对如何在byte[]上高效申请和释放内存的分析,提出了利用位图和完全二叉树优化内存分配的方法,介绍了Netty的实际实现,以及在内存池中如何处理空间分配和碎片化的策略。
订阅专栏 解锁全文

306

被折叠的 条评论
为什么被折叠?



