## 8.3.1 内存分配
netty进行分配时,主要流程比较简单,首先从对象池获取ByteBuf,之后从线程本地缓存MemoryRegionCache中查找内存页,再从Arena的内存池中查找,最后查找Chunk,分配SubPage,最后初始化bytebuf。
1. 从线程的本地缓存中获取PoolThreadCache对象,如果没有,则选择使用空间最少的Arena创建PoolThreadCache实例并保存至线程本地
2. 使用PoolThreadCache的Arena从对象池中获取ByteBuf,对象池中默认没有释放的对象,会创建新对象。Arena根据HAS_UNSAFE判断是PooledUnsafeHeapByteBuf还是PooledHeapByteBuf进行相应处理。
3. 根据请求的内存大小,判断其规格:tiny/small/normal/huge,
* 若大小为tiny(0,512),从本地缓存的PoolThreadCache的MemoryRegionCache中查看是否有释放后的内存可以重用,若有则初始化PooledByteBuf;本地缓存池中没有可重用内存,先根据大小定位到在Arena的tinySubpagePools的位置idx,然后在其中查找可用的PoolSubpage,如果找到内存页,则使用内存页的chunk的初始化PoolSubpage。
* 若大小为small[512,pagesize],逻辑与tiny类似,只是寻找缓存所在Arena中属性有所不同。
* tiny和small在MemoryRegionCache和tinySubpagePools/smallSubpagePools中未找到可用分配的内存页则会调用allocateNormal寻找chunk
* 若大小为normal(pagesize,chunksize],会先从MemoryRegionCache中查找可用的回收后的缓存,如果未找到则会调用allocateNormal寻找chunk
4. 寻找chunk时,先从q050->q025->q000->qInit->q075查找可用的chunk,如果没有找到,会创建PoolChunk对象的实例,创建chunk时会分配实际内存(heap使用byte[],direct使用ByteBuffer.allocateDirect)。找到chunk后,在chunk中查找或创建内存页,最后返回一个Handle。Handle是一个long型整数,记录了chunk和内部的偏移。
5. chunk寻找内存页的过程:根据请求大小计算idx,并找到tinySubpagePools或smallSubpagePools中idx位置的链表head;再根据大小计算在chunk的memoryMap叶子节点的index,如果chunk的subpages数组中index位置为空,说明没有创建PoolSubpage,创建新的内存页之后,并将其加入到chunk的subpages数组。最后修改subpage中的bitmap,讲该内存页加入到tinySubpagePools或smallSubpagePools对应位置的链表中。最后计算出分配出的内存的Handle,`0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;` 内部保存了所分配的内存在chunk中的内存页位置memoryMapIdx和在内存页中的位置bitmapIdx
6. 获得Handle之后,对第2步中获取的ByteBuf进行初始化。
7. 如果chunk是新创建的,还需要加入到Arena的chunklist中。
## 8.3.2 内存释放
调用ByteBuf的release方法可以释放内存,主要分为两步:使用Arena释放ByteBuf,将ByteBuf回收到对象池中。
Arena释放ByteBuf时,如果线程本地PoolThreadCache不为空,查找PoolThreadCache的Caches数组中对应的MemoryRegionCache,将chunk和Handle加入到MemoryRegionCache的queue中。