昨天,我彻底搞懂了Netty内存分配策略( 三 )


这样下次就不需要再去申请空闲页了,直接去池中找就好了 。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)不同外其余完全一致 。
从结构上来看,PoolArena 中主要包含三部分子内存池:

  • tinySubpagePools
  • smallSubpagePools
  • 一系列的 PoolChunkList
tinySubpagePools 和 smallSubpagePools 都是 PoolSubpage 的数组,数组长度分别为 32 和 4 。
PoolChunkList 是一个容器,其内部可以保存一系列的 PoolChunk 对象,并且,Netty 会根据内存使用率的不同,将 PoolChunkList 分为不同等级的容器 。
abstract class PoolArena<T> implements PoolArenaMetric {   enum SizeClass {        Tiny,        Small,        Normal    }  // 该参数指定了tinySubpagePools数组的长度,由于tinySubpagePools每一个元素的内存块差值为16,    // 因而数组长度是512/16,也即这里的512 >>> 4  static final int numTinySubpagePools = 512 >>> 4;    //表示该PoolArena的allocator  final PooledByteBufAllocator parent;  //表示PoolChunk中由Page节点构成的二叉树的最大高度,默认11  private final int maxOrder;  //page的大小,默认8K  final int pageSize;  // 指定了叶节点大小8KB是2的多少次幂,默认为13,该字段的主要作用是,在计算目标内存属于二叉树的    // 第几层的时候,可以借助于其内存大小相对于pageShifts的差值,从而快速计算其所在层数  final int pageShifts;  //默认16MB  final int chunkSize;  // 由于PoolSubpage的大小为8KB=8196,因而该字段的值为    // -8192=>=> 1111 1111 1111 1111 1110 0000 0000 0000    // 这样在判断目标内存是否小于8KB时,只需要将目标内存与该数字进行与操作,只要操作结果等于0,    // 就说明目标内存是小于8KB的,这样就可以判断其是应该首先在tinySubpagePools或smallSubpagePools    // 中进行内存申请  final int subpageOverflowMask;  // 该参数指定了smallSubpagePools数组的长度,默认为4  final int numSmallSubpagePools;  //tinySubpagePools用来分配小于512 byte的Page  private final PoolSubpage<T>[] tinySubpagePools;  //smallSubpagePools用来分配大于等于512 byte且小于pageSize内存的Page  private final PoolSubpage<T>[] smallSubpagePools;  //用来存储用来分配给大于等于pageSize大小内存的PoolChunk  //存储内存利用率50-100%的chunk  private final PoolChunkList<T> q050;  //存储内存利用率25-75%的chunk  private final PoolChunkList<T> q025;  //存储内存利用率1-50%的chunk  private final PoolChunkList<T> q000;  //存储内存利用率0-25%的chunk  private final PoolChunkList<T> qInit;  //存储内存利用率75-100%的chunk  private final PoolChunkList<T> q075;  //存储内存利用率100%的chunk  private final PoolChunkList<T> q100;    //堆内存(heap buffer)  static final class HeapArena extends PoolArena<byte[]> {  }   //堆外直接内存(direct buffer)  static final class DirectArena extends PoolArena<ByteBuffer> {  }}如上所示,PoolArena 是由多个 PoolChunk 组成的大块内存区域,而每个 PoolChunk 则由多个 Page 组成 。


推荐阅读