Netty内存分配

内存分配的目标

  1. 减少内存分配和回收的时间
  2. 减少内部和外部碎片,提高利用率

内存分配的演进

固定大小的Page:

不同大小的Page:

jemalloc(Netty内存分配算法)的核心:Page组合+缓存

Netty内存分配的架构

整合上面提到的架构,通过Page细粒度划分、Page复用、使用率平衡、链表+满二叉树等手段达到缩短响应时间、减少内存碎片的目的。

分配流程

  1. 线程私有缓存查找

尝试从线程私有缓存中查找已释放的空闲块

内存块使用完之后,不会马上归还,而是缓存起来,每一次请求先从缓存获取。

优势:

缓存+复用,提高响应速度

  1. 线程共享缓存查找

如果是tiny或者small,尝试从缓存中查找已释放的空闲块

优势:

缓存+复用,提高响应速度

  1. 找到一个可以分配内存的chunk

从链表中找到一个chunk(Page的集合),在chunk里面分配page。chunk是netty申请内存的基本单位,一个chunk是16M。

把使用率不同的chunk归类在6个链表中,每一次从中间链表开始查找,找到足够空间的chunk就返回

chunk使用率超出当前链表的阈值时,会向左或向右移动到合适的链表中

优势:

如果每次都从高使用率的chunk开始分配,空间不足的概率较高,需要多次才能分配成功;

如果每次都从低使用率的chunk开始分配,整体空间利用率很低,内存碎片较多;

按使用率分类,每次从使用率中等的Chunk分配,保证整体使用率平衡。

  1. 从chunk中分配一个Page

进入chunk中找到一个page,如果需求小于8K会拆分page、分配subpage并且把剩下的放入缓存,如果需求大于8K会分配连续的多个page。

为了减少chunk里面meta信息的占用空间,实际的存储内容为每个节点一个数字,初始值:

分配后:

内存分配基本单位是一个8K的page(树的叶节点),如果需求小于8K,其实是把某个page拆分成tiny/small的subpage,如果大于8K,其实是分配多个page,这样可拆可合的方式满足了不同大小的内存分配需求,减小碎片;

不同大小的内存块分别保存在不同的队列中,从队列头部获取;pageSize有下面几种:

大于8K的分配,比如16K:

计算出内存块应该在倒数第二层,然后从左子节点到右子节点查找,由于每个节点都记录了所在子树是否已被分配。

分配出去后,递归标记所有父节点,表示当前节点及子树已被分配。

小于8K的分配,比如512B:

通过上面的步骤找到叶子结点,一个8K的Page;

把Page拆分成16个大小为512B的SubPage,全部添加到small subpage list里面,并把第一个分配出去,剩下的都缓存下来,等下一次分配。

优势:

Page组合和拆分,更灵活和细粒度的Page大小,减少内存碎片

总结

  • 通过PooledByteBufAllocator进入PoolArena尝试分配内存,每个线程在第一次分配内存的时候从Arena数组中选择一个固定的Arena并绑定,在整个生命周期都只与该Arena交互。
  • PoolArena最重要的部分是PoolChunk,由六个PoolChunkList把所有的PoolChunk组合起来,每个PoolChunkList负责管理一类PoolChunk,由PoolChunk的使用率决定其属于哪一个List,当其使用率达到临界值的时候,会从一个List移动到另一个List,一般情况下,一个PoolChunk产生于qInit,最终会到达q100。
  • Netty向操作系统申请存储空间的单位是PoolChunk,默认大小为16M。PoolChunk由2048个Page组成,每个Page的默认大小为8K,如图中右下所示。Netty把所有的Page保存在一个长度为2048的数组里面,并将其抽象为一棵满二叉树,方便记录内存是否已使用。
  • 大于等于8K且小于等于16M的内存分配请求统称为Normal请求,此类请求会精确到Page的层次;大于16M的内存分配请求称为Huge请求,会使用非池化的方式直接分配;大于等于512个字节但小于8K的请求称为Small请求,大于等于16字节但小于512字节的请求称为Tiny请求,这两类请求可能会把一个Page继续拆分成更小的内存块,并且将拆出来的内存块的引用保存在图中左上角所示的tinySubpagePools和smallSubpagePools中。各类内存块的分配流程将会在后文的源码分析中有所展示。
  • PoolThreadCache是Netty版本的ThreadLocalCache,在不同线程间隔离,用于复用线程已申请过的内存。每一次的内存分配请求都会从PoolThreadCache开始,可能会在(tiny/small)SubpagePools结束,也可能继续到PoolChunk中才会结束。
  • 内存块划分的力度越小有利于减少外部和内部碎片,但会提高内存管理难度,Netty内存管理(或者说jemalloc)是两者的折衷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值