private long toHandle(int bitmapIdx) {
return 0xb000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
}
该值,主要是用一个long类型的值的低32位来表示 memoryMapIdx,用高32位表示 bitmapIdx,这里为什么需要与0xb000000000000000L进行或运算呢?据我的目前掌握的知识看,主要是将0映射为一个数字,主要来区分是在PoolSubpage中分配的,还是内存大于pageSize,在内存释放的时候,可以通过bitmaIdx >> 32 & 0x3FFFFFFF 得出原来的索引。
1.3.1.4 关于 PoolArena allocate代码@4 initBuf详解:
void initBuf(PooledByteBuf buf, long handle, int reqCapacity) {
int memoryMapIdx = (int) handle;//@1
int bitmapIdx = (int) (handle >>> Integer.SIZE); //@2
if (bitmapIdx == 0) {//@3
byte val = value(memoryMapIdx);
assert val == unusable : String.valueOf(val);
buf.init(this, handle, runOffset(memoryMapIdx), reqCapacity, runLength(memoryMapIdx));
} else {//@4
initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
}
}
首先这里对入参 long handle做一个详解:如果需要分配的内存大于pageSize,则返回的高32为为0,低32位为memoryMap的下标id。具体参照下文的allocateRun方法讲解;如果需要分配的内存小于pageSize,则返回的高32为为bitmapIdx,低32位同样表示memoryMap的id。
代码@1:从long中获取memoryMapIdx。
代码@2:从long中获取bitmapIdx。
代码@3:如果bitmapIdx为0,偏移量就是PoolSubpage的偏移量。为什么呢?bitmapIdx为0在内存申请大于pageSize和小于pageSize时都会出现:
-
如果申请的内存数小于pageSize,bitmapIdx表示该pageSubpage是第一次分配。
-
如果申请的内存数大于pageSize,表明该节点所以子节点都是第一次被分片。故偏移量就是memory[id]所代表的偏移量。
代码@4:如果bitmapIdx不为0,需要计算偏移量,与PoolSubpage的elemSize相关,具体请看2)PoolChunk的initBufWithSubpage方法,该方法,最终还是要调用PooledByteBuf的init方法,分配内存,这里只是需要计算偏远量。
1、PooledByteBuf的 int方法
void init(PoolChunk chunk, long handle, int offset, int length, int maxLength) {
assert handle >= 0;
assert chunk != null;
this.chunk = chunk;
this.handle = handle;
memory = chunk.memory; // @1
this.offset = offset; // @2
this.length = length; // @3
this.maxLength = maxLength; // @4
setIndex(0, 0); //@5
tmpNioBuf = null;
initThread = Thread.currentThread();//@6
}
代码@1:PooledByteBuf的内存,直接指向PoolChunk的memory;
代码@2:offset,在memory的起始偏移量。
代码@3:memory的offset + length之间的内存被该PooledByteBuf占用,其他ByteBuf无法使用。
代码@4:maxLength的作用是什么呢?目前我的理解是,PooledByteBuf自动扩容时,只要最终长度不超过maxLength,就可以在当前的内存中完成,不需要去申请新的空间,再进行内存负责。
代码@5:初始化readIndex,writeIndex。
代码@6:记录该PooledByteBuf的初始化线程,方便本地线程池的使用。
2、PoolChunk的iinitBufWithSubpage方法详解
private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx, int reqCapacity) {
assert bitmapIdx != 0;
int memoryMapIdx = (int) handle;
PoolSubpage subpage = subpages[subpageIdx(memoryMapIdx)];
assert subpage.doNotDestroy;
assert reqCapacity <= subpage.elemSize;
buf.init(
this, handle,
runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize, reqCapacity, subpage.elemSize);
}
1.3.2 PoolChunk allocate关于代码@allocateRun,超过pageSize内存分配源码分析
/**
-
Allocate a run of pages (>=1)
-
@param normCapacity normalized capacity
-
@return index in memoryMap
*/
private long allocateRun(int normCapacity) {
int d = maxOrder - (log2(normCapacity) - pageShifts); //@1
int id = allocateNode(d); //@2
if (id < 0) {
return id;
}
freeBytes -= runLength(id); //@3
return id;
}
该方法的实现,就是要在平衡二叉树中要找到一个可以满足normCapacity的节点,从前文的介绍中得知,memoryMap[id]存放的是,该id能分配的最小深度代表的容量。超过pageSize的内存节点,肯定不是在叶子节点。如果让我实现查找合适id的算法,我想应该是这样的:
1、算成需要分配的内存大小是 pag