该文所涉及的netty源码版本为4.1.16。

在一开始需要明确的几个概念

在Netty的内存池的PoolChunk中,先要明确以下几个概念。

  • page: page是chunk中所能申请到的最小内存单位。
  • chunk: 一个chunk是一组page的集合
  • 在PoolChunk中,chunkSize的大小是2maxOrderpageSize,其中2maxOrder是PoolChunk中的完全二叉树叶子结点的数量,pageSize则是单个page的大小。
    综合如上所述,举一个数字上的例子,默认情况下,单个Page的大小为8192,也就是8kb,maxOrder默认情况下是11,因此在这个情况下PoolChunk中的二叉树的叶子节点数量是2048,chunkSize的大小则是2048
    8kb为16M。

PoolChunk的内部完全二叉树结构

PoolChunk中的page通过一颗完全二叉树来达到快速访达及操作,而不需要通过O(n)的时间复杂度来进行遍历,并耗费相当大的空间来记录各个page的使用情况。一颗完全二叉树的结构如下所示:

  • 高度=0 1 个节点 (单个节点表示的大小为chunkSize)
  • 高度=1 2个节点 (单个节点表示的大小为chunkSize/2)
  • 高度=d 2^d个节点 (单个节点表示的大小为chunkSize/2^d)
  • 高度=maxOrder 2^maxOrder个节点 (单个节点的大小为chunkSize/2^{maxOrder},也就是pageSize)

在这棵树的帮助下,当我们要申请x大小的内存的时候 ,得到比x最接近的chunkSize/2^k的大小,也就是说只要从左开始找到k层第一个没有被使用的节点即可开始将其子树的叶子结点的page进行分配。

PoolChunk的二叉树使用状态

单依靠上述的完全二叉树是无法达到内存池设计的目的的,因为缺少了page的使用情况,仍旧需要一个数据结构来辅助记录各个节点的使用情况。
PoolChunk中还给出了一个byte数组memoryMap,大小为完全二叉树所有节点的个数,在之前的例子中这个byte数组就为4096。在初始情况下,这个数组每个位置上的初始指为该位置的节点在完全二叉树中的高度。因此,这个数组memoryMap就有了以下几种状态。

    1. memoryMap[i] = i节点在完全二叉树中的深度,代表当前节点下的子树都还没有被分配。
    1. memoryMap[i] > i节点在完全二叉树中的深度, 这个节点下的子树也就有节点被使用,但是仍有节点处于空闲状态。
    1. memoryMap[i] = maxOrder + 1,这个节点下面的子树已经完全被使用。
      这个Byte数组,就相当于为这个完全二叉树准备了状态与索引存储,可以高效的在二叉树中选择定位所需要指定大小的子树进行分配。

业务逻辑展开

    private int allocateNode(int d) {int id = 1;int initial = - (1 << d); // has last d bits = 0 and rest all = 1byte val = value(id);if (val > d) { // unusablereturn -1;}while (val < d || (id & initial) == 0) { // id & initial == 1 << d for all ids at depth d, for < d it is 0id <<= 1;val = value(id);if (val > d) {id ^= 1;val = value(id);}}byte value = value(id);assert value == d && (id & initial) == 1 << d : String.format("val = %d, id & initial = %d, d = %d",value, id & initial, d);setValue(id, unusable); // mark as unusableupdateParentsAlloc(id);return id;}

allocateNode(int d)方法用来在完全二叉树中以从左开始的顺序获取一颗高度为d的没有被使用过的子树。具体顺序如下:

  • 首先从根节点1开始,判断memoryMap[1]的值,如果大于d,则说明当前的二叉树已经不存在能够分配的节点了。如果小于d,则可以继续往下分配。
  • 如果其左节点在memoryMap的值小于d,则继续从左节点往下寻找。如果大于,则从其右节点开始往下寻找。
  • 在下一层的节点中持续进行上述的判断,直到在书中找到符合高度条件的子树。
    private long allocateRun(int normCapacity) {int d = maxOrder - (log2(normCapacity) - pageShifts);int id = allocateNode(d);if (id < 0) {return id;}freeBytes -= runLength(id);return id;}

allocateRun()方法就是在上文的allocateNode()的前提下,根据指定的大小的内存在二叉树上分配指定大小的子树。比如说在上述16M大小每个page8kb的chunk中寻求64k的内存的时候,需要8个page叶子结点,那么就是需要一个高度为4的完全二叉树,那么也就是只要在PoolChunk中通过allocateNode()方法从完全二叉树的第7层开始从左往右找到一颗可以使用的子树即可。

    private long allocateSubpage(int normCapacity) {// Obtain the head of the PoolSubPage pool that is owned by the PoolArena and synchronize on it.// This is need as we may add it back and so alter the linked-list structure.PoolSubpage<T> head = arena.findSubpagePoolHead(normCapacity);synchronized (head) {int d = maxOrder; // subpages are only be allocated from pages i.e., leavesint id = allocateNode(d);if (id < 0) {return id;}final PoolSubpage<T>[] subpages = this.subpages;final int pageSize = this.pageSize;freeBytes -= pageSize;int subpageIdx = subpageIdx(id);PoolSubpage<T> subpage = subpages[subpageIdx];if (subpage == null) {subpage = new PoolSubpage<T>(head, this, id, runOffset(id), pageSize, normCapacity);subpages[subpageIdx] = subpage;} else {subpage.init(head, normCapacity);}return subpage.allocate();}}

当向PoolChunk申请的内存大小小于pageSize的时候,将直接通过allocateSubpage()方法尝试直接在叶子结点,也就是二叉树的最后一层选择一个空的还未使用的叶子结点,在选择的叶子结点中构造一个PoolSubPage来返回,而不需要耗费整整一个叶子结点导致内存占用浪费。

Netty技术细节源码分析-内存池之PoolChunk设计与实现相关推荐

  1. nginx源码分析—内存池结构ngx_pool_t及内存管理

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

  2. Nginx源码分析-内存池

    本文转自淘宝平台http://www.tbdata.org/archives/1390,不是为了夺他人之美,只是觉得写得很好,怕淘宝万一删掉就找不到了,放在这里保存一下.大家可以直接链接过去,他们那个 ...

  3. Netty技术细节源码分析-FastThreadLocal源码分析

    本文是该篇的修正版 本文的github地址:点此 Netty 的 FastThreadLocal 源码解析 该文中涉及到的 Netty 源码版本为 4.1.6. Netty 的 FastThreadL ...

  4. Netty技术细节源码分析-ByteBuf的内存泄漏原因与检测

    本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty中的ByteBuf为什么会发生内存泄漏 在Netty中,ByetBuf并不是只采用可达性分析来对ByteBuf底层 ...

  5. Netty技术细节源码分析-Recycler对象池原理分析

    本文是该篇的修正版 本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty的对象池Recycler是什么 Recycler是Netty中基于ThreadLocal的轻量 ...

  6. Netty技术细节源码分析-MpscLinkedQueue队列原理分析

    本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. MpscLinkedQueue是什么 在Netty的核心中的核心成员NioEventLoop中,其中任务队列的实现taskQ ...

  7. nginx源码分析—内存池结构ngx_pool_t及内存管理(精辟)

    Content 0.序 1.内存池结构 1.1 ngx_pool_t结构 1.2其他相关结构 1.3 ngx_pool_t的逻辑结构 2.内存池操作 2.1创建内存池 2.2销毁内存池 2.3重置内存 ...

  8. Netty技术细节源码分析-HashedWheelTimer时间轮原理分析

    本文是该篇的修正版 本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty时间轮HashedWheelTimer是什么 Netty的时间轮HashedWheelTime ...

  9. Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求

    Elasticsearch源码分析-线程池(十一) 转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html 线程池 每个节 ...

最新文章

  1. Beta 冲刺 (3/7)
  2. Centos7安装go-1.9.2
  3. Hibernate之一级缓存和二级缓存
  4. C++rat maze老鼠迷宫算法(附完整源码)
  5. HTML DOM getElementsByName() 方法
  6. 八年溯源,如何巧搭区块链
  7. C++ 读取windows服务列表 与操作注册表
  8. 力扣—— 224. 基本计算器(困难)
  9. Chainmaker 查询当前区块高度
  10. 【前端切图】用css画一个卡通形象-小猪佩奇
  11. 用html代码制作一个表单页面,HTML网页表单制作详细讲解
  12. 2014SQLServer还原数据库
  13. 关于宋宝华linux驱动学习视频的读后感
  14. 论文笔记:Geneva、Themis、SymTCP、TCP-Fuzz
  15. 何为数码相机EXIF信息的等效焦距
  16. 紫外线杀菌器:过流式Photoscience紫外线杀菌器
  17. PDF工具Adobe Arcrobat Pro DC下载安装教程
  18. python 列表 元祖_Python_列表与元祖
  19. 【ICLR 2018图神经网络论文解读】Graph Attention Networks (GAT) 图注意力模型
  20. 富斯 fs-i6s 内置18650电池+USB充电

热门文章

  1. 通过中序线索二叉树找某节点的后续前驱☆
  2. BitMap-BitSet(JDK1.8)基本使用入门
  3. Drools集成SpringBoot
  4. WeakReference类详解
  5. 二分实现:查找数组中的峰值元素
  6. spark.sql读取Hive数据报错
  7. vuedraggable嵌套块拖拽_Vue 基于 vuedraggable 实现选中、拖拽、排序效果
  8. SpringBoot 自动装配原理
  9. ethers.js-5-Utilities
  10. dedecms修改数据库信息的路径