一、写在前面
到这里, 想必你已知道了Netty中的内存规格化(SizedClass), Page和SubPage级别的内存分配, 但是具体使用者不应该关心应该申请page还是subpage。而且从过去的经验来说, 申请page/subpage的数量也是个动态值, 如果申请使用完之后就释放那使用内存池的意义就不大。Netty的实现中引入了分配器和内存池。其中分配器面向使用者, 解决统一申请问题。内存池负责完成内存的申请和复用管理, 接下面咱们先聊内存池PoolArena。
二、关键字段
字段名称 | 字段类型 | 含义 |
---|---|---|
q050、q025、q000、qInit、q075、q100 | PoolChunkList | PoolChunk链表, 差别在于其中的PoolChunk使用率 |
smallSubpagePools | PoolSubpage[] | 不同尺寸的PoolSubPage, 由于PoolSubPage本身是链表结构, 数组存储内容为表头元素 |
三、结构示意
四、Normal分配
- PoolArena刚创建时是有分配能力但是没资源的, 内存是延迟到使用者实际请求内存时, 才开始向OS申请并创建PoolChunk;
- 委托给PoolChunk进行具体的Normal分配;
- 将新建的PoolChunk加入到qInit中;
- 后续分配先从PoolChunkList中分配, 如果分配成功则返回, 分配失败则走1,2,3;
- 如果PoolChunkList中某个PoolChunk分配成功则根据PoolChunk的freeBytes标准, 将PoolChunk移动nextList中比如q050中的某个PoolChunk分配runSize后该Chunk剩余空间为0, 则将其移动到q075队列中。
五、Small分配
- 首先Small级别的空间需要在Normal空间中分配, 因此首次分配Small级别的空间, 需要先完成Normal分配;
- 委托PoolChunk完成Small级别的空间分配;
- 前面我们已经知道Small级别的分配结果PoolSubPage其容量是N个requestSize的, 因此一次分配之后会存在剩余, Netty将其维护在subPagePool中;
- 二次分配Small级别空间, 如果requestSize和首次一样, 则直接从存在的PoolSubPage中分配; 如果不一样, 则从PoolChunk中分配新的PoolSubPage, 将将其加入subPagePool中;
六、Huge分配
- Huge空间不进入内存池管理, 直接向OS申请然后返回给使用者;
- 使用者使用完成后直接释放掉;
七、空间回收
- 显然实际的内存空间都是PoolChunk持有, 因此最终委托PoolChunk来回收;
- 如果PoolChunk回收之后, 空间利用率小于最小阈值, 则将其移动到prevList中。比如q025的某个PoolChunk分配回收一个SubPage或者多个Page之后空间利用率低于25%, 则将其移动到q000队列中。当然如果继续回收最终全部释放, 则可能会回到qInit中;
- 如果回到qInit中, PoolChunk会被destory掉, 相关内存也会归还OS;
- 如果所有的PoolChunk都被destory掉, PoolArena就回到最开始的状态;
八、小结
以上是个人对PoolArena的总结, 内容涉及其中的多个PoolChunkList和subPagePool的构造, 空间分配和回收过程中两者的搭配。希望能帮助你理清主干, 而后更好地搞清楚某个具体的分支细节以应对工程问题。