割り当てる
今回適用したバッチのサイズが設定したバッチのサイズと等しく、メモリプールが空でない場合は、メモリのブロックをメモリプールから直接使用できます。
もう1つの状況は、メモリプール全体に10kのメモリが残っているが、今回適用したメモリは32kであり、バッチは16kである可能性があります
が、メッセージの1つは32K-> max(16,32)=現在のバッチ= 32K
では、このような大量のメモリを一度に割り当てることができない場合がありますが、最初に少し割り当てることができます。
割り当てられたメモリのサイズが、適用されるメモリの量ほど大きくない場合。メモリプールは常にメモリを割り当て、ビットごとに割り当てます。他の人を待つとメモリが解放されます。
moreMemory.await
ByteBuffer buffer = free.allocate(size, maxTimeToBlock);
public ByteBuffer allocate(int size, long maxTimeToBlockMs) throws InterruptedException {
//如果你想要申请的内存的大小,如果超过32M
if (size > this.totalMemory)
throw new IllegalArgumentException("Attempt to allocate " + size
+ " bytes, but there is a hard limit of "
+ this.totalMemory
+ " on memory allocations.");
//加锁的代码
this.lock.lock();
try {
// check if we have a free buffer of the right size pooled
//poolableSize 代表的是一个批次的大小,默认情况一下,一个批次的大小是16K。
//如果我们这次申请的批次的大小等于 我们设定好的一个批次的大小
//并且我们的内存池不为空,那么直接从内存池里面获取一个块内存就可以使用了。
//跟我们连接池是一个道理。
//我们场景驱动的方式,第一次进来
//内存池里面里面是没有内存的,所以这儿是获取不到内存。
//poolable Size默认批次的大小
//申请的这个内存的大小是否等于默认的批次的内存大小
//
if (size == poolableSize && !this.free.isEmpty())
return this.free.pollFirst();
// now check if the request is immediately satisfiable with the
// memory on hand or if we need to block
//内存的个数 * 批次的大小 = free的大小
//内存池内存的大小
int freeListSize = this.free.size() * this.poolableSize;
// size: 我们这次要申请的内存
//this.availableMemory + freeListSize 目前可用的总内存
//this.availableMemory + freeListSize 目前可用的总内存 大于你要申请的内存。
if (this.availableMemory + freeListSize >= size) {
// we have enough unallocated or pooled memory to immediately
// satisfy the request
freeUp(size);
//进行内存扣减
this.availableMemory -= size;
lock.unlock();
//直接分配内存
return ByteBuffer.allocate(size);//16k
} else {
//还有一种情况就是,我们整个内存池 还剩10k的内存,但是我们这次申请的内存是32k
//批次可能就是16k,但是我们的一条消息,就是32K -> max(16,32) = 当前批次 = 32K
// we are out of memory and will have to block
//统计分配的内存
int accumulated = 0;
ByteBuffer buffer = null;
//
Condition moreMemory = this.lock.newCondition();
long remainingTimeToBlockNs = TimeUnit.MILLISECONDS.toNanos(maxTimeToBlockMs);
//等待 别人释放内存
this.waiters.addLast(moreMemory);
// loop over and over until we have a buffer or have reserved
// enough memory to allocate one
/**
* 总的分配的思路,可能一下子分配不了这么大的内存,但是可以先有点分配一点。
*
* 如果分配的内存的大小 还是没有要申请的内存大小大。
* 内存池就会一直分配的内存,一点一点的去分配。
* 等着别人会释放内存。
*/
//accumulated 5K+16K=21K 16K
// size 32K
while (accumulated < size) {
long startWaitNs = time.nanoseconds();
long timeNs;
boolean waitingTimeElapsed;
try {
//在等待,等待别人释放内存。
//如果这儿的代码是等待wait操作
//那么我们可以猜想一下,当有人释放内存的时候
//肯定不是得唤醒这儿的代码
//目前代码一直在等待着
//假设,突然有人 往内存池里面还了内存。
//那么这儿的代码就可以被唤醒了。代码就继续往下执行。
// (1) 时间到了
// (2) 被人唤醒了。
waitingTimeElapsed = !moreMemory.await(remainingTimeToBlockNs, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
this.waiters.remove(moreMemory);
throw e;
} finally {
long endWaitNs = time.nanoseconds();
timeNs = Math.max(0L, endWaitNs - startWaitNs);
this.waitTime.record(timeNs, time.milliseconds());
}
if (waitingTimeElapsed) {
this.waiters.remove(moreMemory);
throw new TimeoutException("Failed to allocate memory within the configured max blocking time " + maxTimeToBlockMs + " ms.");
}
remainingTimeToBlockNs -= timeNs;
// check if we can satisfy this request from the free list,
// otherwise allocate memory
//再次尝试看一下,内存池里面有没有数据了。
//如果内存池里面有数据
//并且你申请的内存的大小就是一个批次的大小
//32K 16K
if (accumulated == 0 && size == this.poolableSize && !this.free.isEmpty()) {
// just grab a buffer from the free list
//这儿就可以直接获取到内存。
buffer = this.free.pollFirst();
accumulated = size;
} else {
// we'll need to allocate memory, but we may only get
// part of what we need on this iteration
freeUp(size - accumulated);
// 可以给你分配的内存
int got = (int) Math.min(size - accumulated, this.availableMemory);
//做内存扣减 13K
this.availableMemory -= got;
//累加已经分配了多少内存。
accumulated += got;
}
}
// remove the condition for this thread to let the next thread
// in line start getting memory
Condition removed = this.waiters.removeFirst();
if (removed != moreMemory)
throw new IllegalStateException("Wrong condition: this shouldn't happen.");
// signal any additional waiters if there is more memory left
// over for them
if (this.availableMemory > 0 || !this.free.isEmpty()) {
if (!this.waiters.isEmpty())
this.waiters.peekFirst().signal();
}
// unlock and return the buffer
lock.unlock();
if (buffer == null)
return ByteBuffer.allocate(size);
else
return buffer;
}
} finally {
if (lock.isHeldByCurrentThread())
lock.unlock();
}
}
割り当て解除
返されるメモリのサイズがバッチのサイズと等しい場合、メモリの内容はクリアされ、メモリはメモリプールに入れられます。
解放したメモリのサイズがバッチのサイズではない場合、その後、使用可能なメモリとして分類されます。ガベージコレクションが
メモリを解放するのを待つ(またはメモリが戻された後)と、メモリを待機しているスレッドがウェイクアップします。moreMem.signal()-> moreMemory.await
public void deallocate(ByteBuffer buffer, int size) {
lock.lock();
try {
//如果你还回来的内存的大小 就等于一个批次的大小,
//我们的参数设置的内存是16K,你计算出来一个批次的大小也是16,申请的内存也是16k
//16K 32K
if (size == this.poolableSize && size == buffer.capacity()) {
//内存里面的东西清空
buffer.clear();
//把内存放入到内存池
this.free.add(buffer);
} else {
//但是如果 我们释放的内存的大小
//不是一个批次的大小,那就把归为可用内存
//等着垃圾回收即可
this.availableMemory += size;
}
Condition moreMem = this.waiters.peekFirst();
if (moreMem != null)
//释放了内存(或者是还了内存以后)
//都会唤醒等待内存的线程。
//接下来是不是还是要唤醒正在等待分配内存的线程。
moreMem.signal();
} finally {
lock.unlock();
}
}