Netty内存管理概述

1. 内存管理概述

内存管理的主要目的合理分配内存,减少内存碎片,及时回收资源,提高内存的使用效率。

从操作系统层面来说,各个软件在运行时向操作系统请求对计算机内存资源进行快速的分配,并且在适当的时候释放和回收内存资源。常见的一些算法有slab,buddy,jemalloc等经典算法。

从Netty层面来说,其实质就是先分配一块大内存,然后在内存的分配和回收过程中,使用一些数据结构记录内存使用状态,如果有新的分配请求,根据这些状态信息寻找最合适的位置返回并更新数据结构;内存使用完释放后,同步修改数据结构。Netty的内存管理分为有缓冲池和无缓冲池,有缓冲池的内存分配器会在内存回收时,将信息记录在缓冲池中,下次如果有合适的分配请求则直接从缓冲池中复用。在实践中,分配和回收在有缓冲池的(pooled)直接内存效率更高。

2. 分配算法概述

Netty采用了Jemalloc的思想,这是FreeBSD实现的一种并发分配算法。Jemalloc依赖多个Arena来分配内存,运行中的应用都有固定数量的多个Arena,默认的数量与处理器的个数有关。系统中有多个Arena的原因是由于各个线程进行内存分配时竞争不可避免,这可能会极大的影响内存分配的效率,为了缓解高并发时的线程竞争,Netty允许使用者创建多个分配器(Arena)来分离锁,提高内存分配效率。

线程首次分配/回收内存时,首先会为其分配一个固定的Arena。线程选择Arena时使用round-robin的方式,也就是顺序轮流选取。

每个线程各种保存Arena和缓存池信息,这样可以减少竞争并提高访问效率。Arena将内存分为很多Chunk进行管理,Chunk内部保存Page,以页为单位申请。申请内存分配时,会将分配的规格分为几类:TINY,SAMLL,NORMAL和HUGE,分别对应不同的范围,处理过程也不相同。
Netty

  1. 内存分配的最小单位为16B。
  2. 小于512B的请求为Tiny,小于8KB(PageSize)的请求为Small,小于等于16MB(Chunk Size)的请求为Normal,大于16MB(Chun kSize)的请求为Huge。
  3. 小于512B的请求以16B为起点每次增加16B;大于等于512B的请求则每次加倍。

在Jemalloc中建议Chunk 大小为4MB,Netty默认使用16MB。为了进一步提高内存利用率并减少内部碎片,需要继续将Chunk切分为小的块Page。一般将Chunk切分为2048块,Page的大小为:16MB/2048=8KB。

为了分配内存块保持连续和减少内存碎片,因此Jemalloc使用Buddy内存分配算法。
其实就是采用完全二叉树进行管理,树中每个叶子节点表示一个Page,即树高为12。具有相同父节点的叶子节点称为buddy关系,buddy之间自底向上链接为二叉树,直到根节点。
netty
举个例子:8KB、16KB、8KB为例分析分配过程(每个Page大小8KB):

  1. 8KB:需要一个Page,第11层满足要求,故分配2048节点即Page0;
  2. 16KB:需要两个Page,故需要在第10层进行分配,而1024的子节点2048已分配,从左到右找到满足要求的1025节点,故分配节点1025即Page2和Page3;
  3. 8KB:需要一个Page,第11层满足要求,2048已分配,从左到右找到2049节点即Page1进行分配。

分配结束后,已分配连续的Page0-Page3,这样的连续内存块,大大减少内部碎片并提高内存使用率

Netty中每个Page的默认大小为8KB,在实际使用中,很多业务需要分配更小的内存块比如16B、32B、64B等。为了应对这种需求,需要进一步切分Page成更小的SubPage。SubPage是Jemalloc中内存分配的最小单位,不能再进行切分。SubPage切分的单位并不固定,以第一次请求分配的大小为单位(最小切分单位为16B)。比如,第一次请求分配32B,则Page按照32B均等切分为256块;

3. ByteBuf和分配器

ByteBufAllocator是对外暴露创建ByteBuf的接口,ByteBuf有两个不同的维度的描述,是否堆内存以及是否池化,因此就有四种组合。默认使用的是ByteBufAllocator.DEFAULT,即池化的直接内存。

非池化的ByteBuf和JDK提供的ByteBuffer类似,每次都会从系统中新分配一块内存并绑定到新建的Bytebuf对象中,注意Bytebuf和内存并不等价,虽然它俩往往会一起出现。非池化的Bytebuf的内存可通过Netty提供的引用计数释放或者虚拟机释放。

池化的ByteBuf会讲ByteBuf对象本身和内存空间进行池化。ByteBuf对象可通过对象池实现,而内存则是通过Jmalloc也就是 PoolArena 分配器进行池化。
本文主要对Netty内存分配器ByteBufAllocator进行介绍,以及对Jemelloc思想的描述。后面会对Netty内存分配的池化、分配与释放、内存泄漏检测以及缓存的使用进行深入的分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值