首先介绍些netty内存池的层级结构,主要分为Arena、ChunkList、Chunk、Page、Subpage这5个层级,这几个层级的关系由大到小,如下图所示:
Arena代表1个内存区域,为了优化内存区域的并发访问,netty中内存池是由多个Arena组成的数组,分配时会每个线程按照轮询策略选择1个Arena进行内存分配。
1个Arena由两个PoolSubpage数组和多个ChunkList组成。两个PoolSubpage数组分别为tinySubpagePools和smallSubpagePools。多个ChunkList按照双向链表排列,每个ChunkList里包含多个Chunk,每个Chunk里包含多个Page(默认2048个),每个Page(默认大小为8k字节)由多个Subpage组成。
每个Arena由如下几个ChunkList构成:
- PoolChunkList
qInit:存储内存利用率0-25%的chunk - PoolChunkList
q000:存储内存利用率1-50%的chunk - PoolChunkList
q025:存储内存利用率25-75%的chunk - PoolChunkList
q050:存储内存利用率50-100%的chunk - PoolChunkList
q075:存储内存利用率75-100%的chunk - PoolChunkList
q100:存储内存利用率100%的chunk
每个ChunkList里包含的Chunk数量会动态变化,比如当该chunk的内存利用率变化时会向其它ChunkList里移动。
每个Chunk里默认包含2048个Page。
每个Page包含的Subpage的大小和个数由首次从该Page分配的内存大小决定,1个page默认大小为8k,如果首次在该page中需要分配1k字节,那么该page就被分为8个Subpage,每个Subpage大小为1k。
在PoolArena中申请内存的流程图如下:
- 对于小于pageSize大小的内存,会在tinySubpagePools或smallSubpagePools中分配,tinySubpagePools用于分配小于512字节的内存,smallSubpagePools用于分配大于512小于pageSize的内存。
- 对于大于pageSize小于chunkSize大小的内存,会在PoolChunkList的Chunk中分配。
- 对于大于chunkSize大小的内存,直接创建非池化Chunk来分配内存,并且该Chunk不会放在内存池中重用。
对于在q050、q025、q000、qInit、q075这些PoolChunkList里申请内存的流程图如下:
- 在PoolChunk中,数组组织呈完美二叉树数据结构。二叉树叶子节点为2048个Page,每个Page的父节点用于分配pageSize
*
2大小内存,同理,对于Page叶子节点的父节点的父节点,用于分配pageSize*
4大小的内存,后面以此类推。 - 在初始状态时,tinySubpagePools和smallSubpagePools为空,因此最初分配小于pageSize的内存时,需要新建1个PoolChunk来分配这块小内存,PoolChunk会对Page分类成若干Subpage,然后用Subpage分配这块小内存,最后会把该Subpage放在tinySubpagePools或smallSubpagePools中。