PoolSubpage源码分析-这个方法获取一个long表示的64个比特位置是否还有空余空间
private int findNextAvail() {
// 没有明确的可用位置时则挨个查找
final long[] bitmap = this.bitmap;
final int bitmapLength = this.bitmapLength;
for (int i = 0; i < bitmapLength; i ++) {
long bits = bitmap[i];
//~bits 把字节0变1 1变0,如果一个long类型64个比特全部为1,那么反过来得0,说明没有位置了。
// 说明这个位置段中还有可以分配的element
if (~bits != 0) {
return findNextAvail0(i, bits);
}
}
return -1;
}
PoolSubpage源码分析-这个方法查询出一个long类型64个比特当中可用的比特位,在加上一个基数,就计算出了对应的内存快的位置。比如当前内存按16字节分块,那么8192/16=512,一共内存分成512块, 在bits当中找到第一个比特为0的位置,比如70,假设基础为64,那么64+70=134就是对应空闲的内存快。
private int findNextAvail0(int i, long bits) {
final int maxNumElems = this.maxNumElems;
//重点:首先右移位6为,相当于乘以8, 因为i的范围从0-7
//i=0的long的64个比特表示0-63个位置, i=1的long表示64-127的位置。
//所以baseVal相当于这个基数,如果i=0,那么baseVal=0,如果i=1,baseVal=64, i=2,baseVal=128
final int baseVal = i << 6;
//循环64次,依次拿出long的每个比特进行判断
for (int j = 0; j < 64; j ++) {
//&俩边为1结果为1否则为0
//bits & 1 相当于判断bits的最后一个比特位,
// 如果该位置的值为0,表示还未分配
if ((bits & 1) == 0) {
// | 俩边为0结果为0 否则为1
//j从0-63,baseVal | j 相当于 baseVal+j, j就是long里面可用比特的位置,但是需要加上基数。
int val = baseVal | j;
//这里要判断一下,因为如果当前的ElementSize不是16,假设为32,那么maxNumElems=256
//bitmap长度为8,64x8=512,表示512个位置,但其实最大位置也就256,所以需要判断一下,只有ElementSize=16时这个判断才没有意义,因为16会被分成512份,正好。
if (val < maxNumElems) {
return val;
} else {
break;
}
}
//否则无符号右移1为,相当于把原来bits的第二个比特移到第一个位置。
bits >>>= 1;
}
return -1;
}
PoolSubpage源码分析-分配一个可用的内存分片。
long allocate() {
//这里不知道什么意思
if (elemSize == 0) {
return toHandle(0);
}
//如果可用的内存分片为0,或者doNotDestroy=false
//说明无内存可用,返回-1
if (numAvail == 0 || !doNotDestroy) {
return -1;
}
//找到一个可用的内存分片的index,假设65
final int bitmapIdx = getNextAvail();
//右移6为等于除以64, q=1,说明要放在bitmap[1]这个位置
//用bitmap[1]这个long的比特表示120这个位置的状态。
int q = bitmapIdx >>> 6;
//把高位全部去掉,只留下一个0-63的数(正好64种可能)
//r表示0-63当中的某一个位置 r=1,
//0011010101...(56比特)+(8比特)000000r1, r=65所在的比特位置
//0011010101...(56比特)+(8比特)0000000r, r=64所在的比特位置
int r = bitmapIdx & 63;
//把r移到最右面,必须等于0,所以为被分配
assert (bitmap[q] >>> r & 1) == 0;
//1L左移R位,相当于复制 在|拼接原始值
bitmap[q] |= 1L << r;
//如果可用内存分片为0,则从队列中移除
if (-- numAvail == 0) {
removeFromPool();
}
//返回一个long类型的handle,里面包含了内存的index,还有当前subpage所属的16M内存当中的index
return toHandle(bitmapIdx);
}
PoolSubpage源码分析,这个方法把3个数字按比特排列方式拼接为1个数字,等使用时在把这三个数字还原。
private long toHandle(int bitmapIdx) {
//把3个数字拼接为一个,一个long类型64个比特,拼接在一起个占个的位置,等需要时在进行拆分得到原先的3个数字。
return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
}
PoolSubpage源码分析添加到队列和从队列中移除,当前对象会加入到arena当中管理。
//把队列插入到头的后面, 比如原来的顺序为 head - x - y
//当前对象为this,那么拼接后的顺序为 head - this -x -y
private void addToPool(PoolSubpage<T> head) {
assert prev == null && next == null;
//当前对象的前一个指head
prev = head;
//当前对象的后一个指head的下一个
next = head.next;
next.prev = this;
head.next = this;
}
//把当前对象从链表中移除
//head - x - this - y 移除后 head - x -y
private void removeFromPool() {
assert prev != null && next != null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
}
//构造方法
PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) {
//所属chunk
this.chunk = chunk;
//所属chunk整个内存分块的下标值,默认chunk为16M,每页大小8192,分为2048个块,那么memoryMapIdx表代表了哪一块
this.memoryMapIdx = memoryMapIdx;
//整体内存的偏移量,比如整体内存为16M的byte素组,那runOffset就是数组下标的起点
this.runOffset = runOffset;
//每页大小,默认8192
this.pageSize = pageSize;
//long类型素组, pageSize / 16 / 64
//因为最小单元把当前8192字节拆分为16个字节一个单元,8192/16=512 个单元
//一个long的64个比特位表示64个单元,那么需要512/64=8个long类型的数字表示
bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64
init(head, elemSize);
}
void init(PoolSubpage<T> head, int elemSize) {
//设置不可回收
doNotDestroy = true;
//单元大小,16 或 32 或 64 。。。。。
this.elemSize = elemSize;
//假设16比特位单元大小
if (elemSize != 0) {
//8192/16=512
//那么maxNumElems 和 numAvail = 512
maxNumElems = numAvail = pageSize / elemSize;
//可用位置从0开始
nextAvail = 0;
//512 除以64 = 8,表示bitmap数组需要使用几个元素,当单元大小为16时,需要8个元素
//这也是最多的一种情况,如果单元大小为32时,maxNumElems=256,那么256/64=4,那么需要4个元素就能满足情况了
bitmapLength = maxNumElems >>> 6;
if ((maxNumElems & 63) != 0) {
bitmapLength ++;
}
//把元素都设置为0
for (int i = 0; i < bitmapLength; i ++) {
bitmap[i] = 0;
}
}
//加入到arena的队列中管理
addToPool(head);
}
boolean free(PoolSubpage<T> head, int bitmapIdx) {
if (elemSize == 0) {
return true;
}
//假设bitmapIdx=65
//除以64计算出数组下标 q=1
int q = bitmapIdx >>> 6;
//计算出所在0-63当中的位置,r=1
int r = bitmapIdx & 63;
//把值移到最右面 必须不能为0
assert (bitmap[q] >>> r & 1) != 0;
//如果相对应位值相同,则结果为0,否则为1
//左移1为,得到 xxxx.....1x(64bit)
//把xxxx.....1x 变为xxxx.....0x,其实就是把r位置的比特设置为0
bitmap[q] ^= 1L << r;
//设置下一个可用位置为当前的bitmapIdx
setNextAvail(bitmapIdx);
//先判断在++,如果numAvail ==0 说明没在池中,需要加入池,然后++
if (numAvail ++ == 0) {
addToPool(head);
return true;
}
//如果可用单元没达到最大值,说明还有空余空间
if (numAvail != maxNumElems) {
return true;
} else {
//如果Subpage空闲
// Subpage not in use (numAvail == maxNumElems)
//判断一下当前对象是不是arena数组中head的下一个元素,如果是则保留当前对象,否则将其从pool中移除。
//目的:这样尽可能的保证arena分配小内存时能直接从pool中取,而不用再到chunk中去获取。
if (prev == next) {
// Do not remove if this subpage is the only one left in the pool.
return true;
}
// Remove this subpage from the pool if there are other subpages left in the pool.
//把当前对象从队列移除,返回false,调用者会根据返回值做处理,会把他回收掉,同时把父类对应的8192空间释放为可用。
doNotDestroy = false;
removeFromPool();
return false;
}
}