Recycler用來實現(xiàn)對象池搏熄,其中對應(yīng)堆內(nèi)存和直接內(nèi)存的池化實現(xiàn)分別是PooledHeapByteBuf和PooledDirectByteBuf速警。Recycler主要提供了3個方法:
- get():獲取一個對象咧最。
- recycle(T, Handle):回收一個對象后控,T為對象泛型扮碧。
- newObject(Handle):當沒有可用對象時創(chuàng)建對象的實現(xiàn)方法白华。
Recycler的UML圖如下:
Recycler關(guān)聯(lián)了4個核心類:
- DefaultHandle:對象的包裝類走越,在Recycler中緩存的對象都會包裝成DefaultHandle類椭豫。
- Stack:存儲本線程回收的對象。對象的獲取和回收對應(yīng)Stack的pop和push旨指,即獲取對象時從Stack中pop出1個DefaultHandle赏酥,回收對象時將對象包裝成DefaultHandle push到Stack中。Stack會與線程綁定谆构,即每個用到Recycler的線程都會擁有1個Stack裸扶,在該線程中獲取對象都是在該線程的Stack中pop出一個可用對象。
- WeakOrderQueue:存儲其它線程回收到本線程stack的對象低淡,當某個線程從Stack中獲取不到對象時會從WeakOrderQueue中獲取對象姓言。每個線程的Stack擁有1個WeakOrderQueue鏈表,鏈表每個節(jié)點對應(yīng)1個其它線程的WeakOrderQueue蔗蹋,其它線程回收到該Stack的對象就存儲在這個WeakOrderQueue里何荚。
- Link: WeakOrderQueue中包含1個Link鏈表,回收對象存儲在鏈表某個Link節(jié)點里猪杭,當Link節(jié)點存儲的回收對象滿了時會新建1個Link放在Link鏈表尾餐塘。
整個Recycler回收對象存儲結(jié)構(gòu)如下圖所示:
下面分析下源碼,首先看下Recycler.recycle(T, Handle)方法皂吮,用于回收1個對象:
public final boolean recycle(T o, Handle handle) {
if (handle == NOOP_HANDLE) {
return false;
}
DefaultHandle h = (DefaultHandle) handle;
if (h.stack.parent != this) {
return false;
}
if (o != h.value) {
throw new IllegalArgumentException("o does not belong to handle");
}
h.recycle();
return true;
}
回收1個對象會調(diào)用該對象DefaultHandle.recycle()方法戒傻,如下:
public void recycle() {
stack.push(this);
}
回收1個對象(DefaultHandle)就是把該對象push到stack中。
void push(DefaultHandle item) {
Thread currentThread = Thread.currentThread();
if (thread == currentThread) {
// The current Thread is the thread that belongs to the Stack, we can try to push the object now.
/**
* 如果該stack就是本線程的stack蜂筹,那么直接把DefaultHandle放到該stack的數(shù)組里
*/
pushNow(item);
} else {
// The current Thread is not the one that belongs to the Stack, we need to signal that the push
// happens later.
/**
* 如果該stack不是本線程的stack需纳,那么把該DefaultHandle放到該stack的WeakOrderQueue中
*/
pushLater(item, currentThread);
}
}
這里分為兩種情況,當stack是當前線程對應(yīng)的stack時艺挪,執(zhí)行pushNow(item)方法不翩,直接把對象放到該stack的DefaultHandle數(shù)組中,如下:
/**
* 直接把DefaultHandle放到stack的數(shù)組里,如果數(shù)組滿了那么擴展該數(shù)組為當前2倍大小
* @param item
*/
private void pushNow(DefaultHandle item) {
if ((item.recycleId | item.lastRecycledId) != 0) {
throw new IllegalStateException("recycled already");
}
item.recycleId = item.lastRecycledId = OWN_THREAD_ID;
int size = this.size;
if (size >= maxCapacity || dropHandle(item)) {
// Hit the maximum capacity or should drop - drop the possibly youngest object.
return;
}
if (size == elements.length) {
elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
}
elements[size] = item;
this.size = size + 1;
}
當stack是其它線程的stack時口蝠,執(zhí)行pushLater(item, currentThread)方法器钟,將對象放到WeakOrderQueue中,如下:
private void pushLater(DefaultHandle item, Thread thread) {
/**
* Recycler有1個stack->WeakOrderQueue映射妙蔗,每個stack會映射到1個WeakOrderQueue傲霸,這個WeakOrderQueue是該stack關(guān)聯(lián)的其它線程WeakOrderQueue鏈表的head WeakOrderQueue。
* 當其它線程回收對象到該stack時會創(chuàng)建1個WeakOrderQueue中并加到stack的WeakOrderQueue鏈表中眉反。
*/
Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
WeakOrderQueue queue = delayedRecycled.get(this);
if (queue == null) {
/**
* 如果delayedRecycled滿了那么將1個偽造的WeakOrderQueue(DUMMY)放到delayedRecycled中昙啄,并丟棄該對象(DefaultHandle)
*/
if (delayedRecycled.size() >= maxDelayedQueues) {
// Add a dummy queue so we know we should drop the object
delayedRecycled.put(this, WeakOrderQueue.DUMMY);
return;
}
// Check if we already reached the maximum number of delayed queues and if we can allocate at all.
/**
* 創(chuàng)建1個WeakOrderQueue
*/
if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
// drop object
return;
}
delayedRecycled.put(this, queue);
} else if (queue == WeakOrderQueue.DUMMY) {
// drop object
return;
}
/**
* 將對象放入到該stack對應(yīng)的WeakOrderQueue中
*/
queue.add(item);
}
static WeakOrderQueue allocate(Stack<?> stack, Thread thread) {
// We allocated a Link so reserve the space
/**
* 如果該stack的可用共享空間還能再容下1個WeakOrderQueue,那么創(chuàng)建1個WeakOrderQueue禁漓,否則返回null
*/
return reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)
? new WeakOrderQueue(stack, thread) : null;
}
WeakOrderQueue的構(gòu)造函數(shù)如下跟衅,WeakOrderQueue實現(xiàn)了多線程環(huán)境下回收對象的機制,當由其它線程回收對象到stack時會為該stack創(chuàng)建1個WeakOrderQueue播歼,這些由其它線程創(chuàng)建的WeakOrderQueue會在該stack中按鏈表形式串聯(lián)起來伶跷,每次創(chuàng)建1個WeakOrderQueue會把該WeakOrderQueue作為該stack的head WeakOrderQueue:
private WeakOrderQueue(Stack<?> stack, Thread thread) {
head = tail = new Link();
owner = new WeakReference<Thread>(thread);
/**
* 每次創(chuàng)建WeakOrderQueue時會更新WeakOrderQueue所屬的stack的head為當前WeakOrderQueue, 當前WeakOrderQueue的next為stack的之前head秘狞,
* 這樣把該stack的WeakOrderQueue通過鏈表串起來了叭莫,當下次stack中沒有可用對象需要從WeakOrderQueue中轉(zhuǎn)移對象時從WeakOrderQueue鏈表的head進行scavenge轉(zhuǎn)移到stack的對DefaultHandle數(shù)組。
*/
synchronized (stack) {
next = stack.head;
stack.head = this;
}
availableSharedCapacity = stack.availableSharedCapacity;
}
下面再看Recycler.get()方法:
public final T get() {
if (maxCapacity == 0) {
return newObject(NOOP_HANDLE);
}
Stack<T> stack = threadLocal.get();
DefaultHandle handle = stack.pop();
if (handle == null) {
handle = stack.newHandle();
handle.value = newObject(handle);
}
return (T) handle.value;
}
取出該線程對應(yīng)的stack烁试,從stack中pop出1個DefaultHandle雇初,返回該DefaultHandle的真正對象。
下面看stack.pop()方法:
DefaultHandle pop() {
int size = this.size;
if (size == 0) {
if (!scavenge()) {
return null;
}
size = this.size;
}
size --;
DefaultHandle ret = elements[size];
elements[size] = null;
if (ret.lastRecycledId != ret.recycleId) {
throw new IllegalStateException("recycled multiple times");
}
ret.recycleId = 0;
ret.lastRecycledId = 0;
this.size = size;
return ret;
}
如果該stack的DefaultHandle數(shù)組中還有對象可用减响,那么從該DefaultHandle數(shù)組中取出1個可用對象返回靖诗,如果該DefaultHandle數(shù)組沒有可用的對象了,那么執(zhí)行scavenge()方法支示,將head WeakOrderQueue中的head Link中的DefaultHandle數(shù)組轉(zhuǎn)移到stack的DefaultHandle數(shù)組刊橘,scavenge方法如下:
boolean scavenge() {
// continue an existing scavenge, if any
if (scavengeSome()) {
return true;
}
// reset our scavenge cursor
prev = null;
cursor = head;
return false;
}
具體執(zhí)行了scavengeSome()方法,清理WeakOrderQueue中部分DefaultHandle到stack颂鸿,每次盡可能清理head WeakOrderQueue的head Link的全部DefaultHandle促绵,如下:
boolean scavengeSome() {
WeakOrderQueue cursor = this.cursor;
if (cursor == null) {
cursor = head;
if (cursor == null) {
return false;
}
}
boolean success = false;
WeakOrderQueue prev = this.prev;
do {
/**
* 將當前WeakOrderQueue的head Link的DefaultHandle數(shù)組轉(zhuǎn)移到stack的DefaultHandle數(shù)組中
*/
if (cursor.transfer(this)) {
success = true;
break;
}
WeakOrderQueue next = cursor.next;
if (cursor.owner.get() == null) {
if (cursor.hasFinalData()) {
for (;;) {
if (cursor.transfer(this)) {
success = true;
} else {
break;
}
}
}
if (prev != null) {
prev.next = next;
}
} else {
prev = cursor;
}
cursor = next;
} while (cursor != null && !success);
this.prev = prev;
this.cursor = cursor;
return success;
}
WeakOrderQueue.transfer()方法如下翘贮,將WeakOrderQueue的head Link中的DefaultHandle數(shù)組遷移到stack中:
boolean transfer(Stack<?> dst) {
Link head = this.head;
if (head == null) {
return false;
}
/**
* 如果head Link的readIndex到達了Link的容量LINK_CAPACITY昂秃,說明該Link已經(jīng)被scavengge完了。
* 這時需要把下一個Link作為新的head Link粤攒。
*/
if (head.readIndex == LINK_CAPACITY) {
if (head.next == null) {
return false;
}
this.head = head = head.next;
}
final int srcStart = head.readIndex;
/**
* head Link的回收對象數(shù)組的最大位置
*/
int srcEnd = head.get();
/**
* head Link可以scavenge的DefaultHandle的數(shù)量
*/
final int srcSize = srcEnd - srcStart;
if (srcSize == 0) {
return false;
}
final int dstSize = dst.size;
/**
* 每次會盡可能scavenge整個head Link栽渴,如果head Link的DefaultHandle數(shù)組能全部遷移到stack中尖坤,stack的DefaultHandle數(shù)組預(yù)期容量
*/
final int expectedCapacity = dstSize + srcSize;
/**
* 如果預(yù)期容量大于stack的DefaultHandle數(shù)組最大長度,說明本次無法將head Link的DefaultHandle數(shù)組全部遷移到stack中
*/
if (expectedCapacity > dst.elements.length) {
final int actualCapacity = dst.increaseCapacity(expectedCapacity);
srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
}
if (srcStart != srcEnd) {
/**
* head Link的DefaultHandle數(shù)組
*/
final DefaultHandle[] srcElems = head.elements;
/**
* stack的DefaultHandle數(shù)組
*/
final DefaultHandle[] dstElems = dst.elements;
int newDstSize = dstSize;
/**
* 遷移head Link的DefaultHandle數(shù)組到stack的DefaultHandle數(shù)組
*/
for (int i = srcStart; i < srcEnd; i++) {
DefaultHandle element = srcElems[i];
if (element.recycleId == 0) {
element.recycleId = element.lastRecycledId;
} else if (element.recycleId != element.lastRecycledId) {
throw new IllegalStateException("recycled already");
}
srcElems[i] = null;
if (dst.dropHandle(element)) {
// Drop the object.
continue;
}
element.stack = dst;
dstElems[newDstSize ++] = element;
}
/**
* 當head節(jié)點的對象全都轉(zhuǎn)移給stack后闲擦,取head下一個節(jié)點作為head糖驴,下次轉(zhuǎn)移的時候再從新的head轉(zhuǎn)移回收的對象
*/
if (srcEnd == LINK_CAPACITY && head.next != null) {
// Add capacity back as the Link is GCed.
reclaimSpace(LINK_CAPACITY);
this.head = head.next;
}
/**
* 遷移完成后更新原始head Link的readIndex
*/
head.readIndex = srcEnd;
if (dst.size == newDstSize) {
return false;
}
dst.size = newDstSize;
return true;
} else {
// The destination stack is full already.
return false;
}
}