线程首次分配/回收内存时,首先会为其分配一个固定的 Arena 。线程选择 Arena 时使用 round-robin 的方式,也就是顺序轮流选取 。
每个线程各种保存 Arena 和缓存池信息,这样可以减少竞争并提高访问效率 。Arena 将内存分为很多 Chunk 进行管理,Chunk 内部保存 Page,以页为单位申请 。申请内存分配时,会将分配的规格分为几类:TINY,SAMLL,NORMAL 和 HUGE,分别对应不同的范围,处理过程也不相同 。
文章插图
tiny 代表了大小在 0-512B 的内存块;
small 代表了大小在 512B-8K 的内存块;
normal 代表了大小在 8K-16M 的内存块;
huge 代表了大于 16M 的内存块 。
每个块里面又定义了更细粒度的单位来分配数据:
- Chunk:一个 Chunk 的大小是 16M,Chunk 是 Netty 对操作系统进行内存申请的单位,后续所有的内存分配都是在 Chunk 里面进行操作 。
- Page:Chunk 内部以 Page 为单位分配内存,一个 Page 大小为 8K 。当我们需要 16K 的空间时,Netty 就会从一个 Chunk 中找到两个 Page 进行分配 。
- Subpage 和 element:element 是比 Page 更小的单位,当我们申请小于 8K 的内存时,Netty 会以 element 为单位进行内存分配 。element 没有固定大小,具体由用户的需求决定 。Netty 通过 Subpage 管理 element,Subpage 是由 Page 转变过来的 。当我们需要 1K 的空间时,Netty 会把一个 Page 变成 Subpage,然后把 Subpage 分成 8 个 1K 的 element 进行分配 。
文章插图
初次申请内存的时候,Netty 会从一整块内存(Chunk)中分出一部分来给用户使用,这部分工作是由 Arena 来完成 。而当用户使用完毕释放内存的时候,这些被分出来的内存会按不同规格大小放在 PoolThreadCache 中缓存起来 。当下次要申请内存的时候,就会先从 PoolThreadCache 中找 。
Chunk、Page、Subpage 和 element 都是 Arena 中的概念,Arena 的工作就是从一整块内存中分出合适大小的内存块 。Arena 中最大的内存单位是 Chunk,这是 Netty 向操作系统申请内存的单位 。而一块 Chunk(16M) 申请下来之后,内部会被分成 2048 个 Page(8K),当用户向 Netty 申请超过 8K 内存的时候,Netty 会以 Page 的形式分配内存 。
Chunk 内部通过伙伴算法管理 Page,具体实现为一棵完全平衡二叉树:
文章插图
二叉树中所有子节点管理的内存也属于其父节点 。当我们要申请大小为 16K 的内存时,我们会从根节点开始不断寻找可用的节点,一直到第 10 层 。那么如何判断一个节点是否可用呢?Netty 会在每个节点内部保存一个值,这个值代表这个节点之下的第几层还存在未分配的节点 。比如第 9 层的节点的值如果为 9,就代表这个节点本身到下面所有的子节点都未分配;如果第 9 层的节点的值为 10,代表它本身不可被分配,但第 10 层有子节点可以被分配;如果第 9 层的节点的值为 12,此时可分配节点的深度大于了总深度,代表这个节点及其下面的所有子节点都不可被分配 。下图描述了分配的过程:
文章插图
对于小内存(小于4096)的分配还会将 Page 细化成更小的单位 Subpage 。Subpage 按大小分有两大类:
- Tiny:小于 512 的情况,最小空间为 16,对齐大小为 16,区间为[16,512),所以共有 32 种情况 。
- Small:大于等于 512 的情况,总共有四种,512,1024,2048,4096 。
第一次申请小内存空间的时候,需要先申请一个空闲页,然后将该页转成 PoolSubpage,再将该页设为已被占用,最后再把这个 PoolSubpage 存到 PoolSubpage 池中 。这样下次就不需要再去申请空闲页了,直接去池中找就好了 。Netty 中有 36 种 PoolSubpage,所以用 36 个 PoolSubpage 链表表示 PoolSubpage 池 。
因为单个 PoolChunk 只有 16M,这远远不够用,所以会很很多很多 PoolChunk,这些 PoolChunk 组成一个链表,然后用 PoolChunkList 持有这个链表 。
我们先从内存分配器 PoolArena 来分析 Netty 中的内存是如何分配的,Area 的工作就是从一整块内存中协调如何分配合适大小的内存给当前数据使用 。PoolArena 是 Netty 的内存池实现抽象类,其内部子类为 HeapArena 和 DirectArena,HeapArena 对应堆内存(heap buffer),DirectArena 对应堆外直接内存(direct buffer),两者除了操作的内存(byte[]和ByteBuffer)不同外其余完全一致 。
推荐阅读
- 茶艺中的弄茶手法,中国传统茶艺介绍
- 茶叶中的糖类,那种茶叶解酒效果最好
- 法律、法规、标准中的“必须、应当、宜、可”的涵义和区别
- 普洱茶中的龙珠茶是什么茶?口感怎么样?
- 带实例 详解JS中的事件机制
- 茶叶的维生素K介绍,红茶中的维生素及其他物质介绍
- 紫水晶 --水晶之王
- 极速推怎么更换投放中的宝贝 淘宝极速推怎么使用
- 十八岁的天空中的薄荷红茶扮演者?[红茶]
- JavaScript中的函数式编程