在这篇《netty源码阅读之性能优化工具类之Recycler获取对象》文章里面,我们还有一个scavenge()方法没有解析,也就是在别的线程里面回收对象。下面我们开始介绍,从这个方法开始进入:
boolean scavenge() {
// continue an existing scavenge, if any
if (scavengeSome()) {
return true;
}
// reset our scavenge cursor
prev = null;
cursor = head;
return false;
}
如果scavengeSome也就是回收到对象了,那就返回true。否则就重置,这个cursor就是当前开始搜索的节点,也就是这次没有回收到,下次从头节点开始搜索。
然后看scavengeSome方法:
boolean scavengeSome() {
WeakOrderQueue cursor = this.cursor;
if (cursor == null) {
cursor = head;
if (cursor == null) {
return false;
}
}
boolean success = false;
WeakOrderQueue prev = this.prev;
do {
if (cursor.transfer(this)) {
success = true;
break;
}
WeakOrderQueue next = cursor.next;
if (cursor.owner.get() == null) {
// If the thread associated with the queue is gone, unlink it, after
// performing a volatile read to confirm there is no data left to collect.
// We never unlink the first queue, as we don't want to synchronize on updating the head.
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 cursor = this.cursor;
if (cursor == null) {
cursor = head;
if (cursor == null) {
return false;
}
}
就说cursor为空,就指向头,头也为空,就说明这个stack没有和其他线程相关的WeakOrderQueue了,返回false。
下面有个while循环,这个循环的意思就是我不停地往WeakOrderQueue获取对象。
看这个:
if (cursor.transfer(this)) {
success = true;
break;
}
意思就是把当前cursor也就是WeakOrderQueue里面的对象装换为this(也就是当前stack)的对象。回收成功的话就可以跳出循环并返回了。
如果没有回收成功,那就调到cursor的下一个节点:
WeakOrderQueue next = cursor.next;
cursor.owner.get()代表与当前WeakOrderQueue关联的线程,owner是一个弱应用,我们上一篇文章分析过。
如果关联的线程不在了,就要做一些清理工作,把WeakOrderQueue移除掉等。看源码:
if (cursor.owner.get() == null) {
// If the thread associated with the queue is gone, unlink it, after
// performing a volatile read to confirm there is no data left to collect.
// We never unlink the first queue, as we don't want to synchronize on updating the head.
if (cursor.hasFinalData()) {
for (;;) {
if (cursor.transfer(this)) {
success = true;
} else {
break;
}
}
}
if (prev != null) {
prev.next = next;
}
}
如果节点有数据,也就是cursor.hasFinalData那就把节点里面的数据移到stack里面:cursor.trasfer(this))。如果成功,那就继续循环,由于我们cursor.transfer每次传递一个Link而已,所以需要用一个for循环不停地把WeakOrderQueue的Link传输到stack里面。
看这个代码:
if (prev != null) {
prev.next = next;
}
就是把这个cursor节点释放。
如果关联的线程还在,就把pre节点和cursor节点往下移,知道cursor为null也就是没有下一个节点,到尾了:
else {
prev = cursor;
}
cursor = next;
} while (cursor != null && !success);
那么我们现在分析cursor.transfer(this)这个方法:
// transfer as many items as we can from this queue to the stack, returning true if any were transferred
@SuppressWarnings("rawtypes")
boolean transfer(Stack<?> dst) {
Link head = this.head;
if (head == null) {
return false;
}
if (head.readIndex == LINK_CAPACITY) {
if (head.next == null) {
return false;
}
this.head = head = head.next;
}
final int srcStart = head.readIndex;
int srcEnd = head.get();
final int srcSize = srcEnd - srcStart;
if (srcSize == 0) {
return false;
}
final int dstSize = dst.size;
final int expectedCapacity = dstSize + srcSize;
if (expectedCapacity > dst.elements.length) {
final int actualCapacity = dst.increaseCapacity(expectedCapacity);
srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
}
if (srcStart != srcEnd) {
final DefaultHandle[] srcElems = head.elements;
final DefaultHandle[] dstElems = dst.elements;
int newDstSize = dstSize;
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;
}
if (srcEnd == LINK_CAPACITY && head.next != null) {
// Add capacity back as the Link is GCed.
reclaimSpace(LINK_CAPACITY);
this.head = head.next;
}
head.readIndex = srcEnd;
if (dst.size == newDstSize) {
return false;
}
dst.size = newDstSize;
return true;
} else {
// The destination stack is full already.
return false;
}
}
看第一段:
Link head = this.head;
if (head == null) {
return false;
}
这个既说明我们的头结点Link没有数据,也就是整一个WeakOrderQueue都没有数据了直接返回。
然后:
if (head.readIndex == LINK_CAPACITY) {
if (head.next == null) {
return false;
}
this.head = head = head.next;
}
在head的readIndex已经达到了LINK_CAPACITY之后,head里面的所有数据都没有了,那就把head指向下一个节点,head就可以丢弃了,因为没有对象指向它了。如果head的next为空,说明整个WeakOrderQueue都没有数据了返回false。
能够走到下面,就证明head里面有handle对象,然后我们找到head的readIndex,就表明我们可以从这个地方开始取对象,看源码:
final int srcStart = head.readIndex;
然后看结束位置:
int srcEnd = head.get();
由于head是Link,link继承自AtomicInteger,link的get就代表link的长度,就表明我里面移动有多少对象。
然后:
final int srcSize = srcEnd - srcStart;
if (srcSize == 0) {
return false;
}
看这个srcSize就是现在我需要传输多少个对象到stack里面,如果srcSize=0,就说明没有对象传输,返回false。
继续:
final int dstSize = dst.size;
final int expectedCapacity = dstSize + srcSize;
dst.size代表stack里面有的数据的大小,加上srcSize就是传输之后的大小。
然后:
if (expectedCapacity > dst.elements.length) {
final int actualCapacity = dst.increaseCapacity(expectedCapacity);
srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
}
由于我们的stack也是用数组实现的,所以要传输的数据不能超过数组的大小,如果超过了就扩容,最后算出srcEnd,srcEnd就是我们需要传输的head的结束的位置。actualCapacity-dstSize就是实际能传输的大小,实际能传输的大小加上srcStart就是srcEnd,这个srcEnd也不能操作原来head的结束位置,所以取两者的最小值。
接下去这一段:
final DefaultHandle[] srcElems = head.elements;
final DefaultHandle[] dstElems = dst.elements;
int newDstSize = dstSize;
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;
}
这里就是赋值操作。
有个细节:
if (element.recycleId == 0) {
element.recycleId = element.lastRecycledId;
} else if (element.recycleId != element.lastRecycledId) {
throw new IllegalStateException("recycled already");
}
如果没有被回收过也就是element.recycleId==0,就是recycledId赋值lastRecycledId,而这个lastRecycleId我们前面也分析过,就是这个WeakOrderQueue的id。那么如果被回收过,并且和当前的id不同也就是element.recycleId!=element.lastRecycledId,那么就可能已经在其他线程回收过,抛出异常。
srcElems[i]=null就是把Link对应的位置置空。
dropHandle我们前面文章也分析过,控制我们回收的频率。
最后element.stack=dst和dstElems[newDstSize++]=element就是真正的赋值操作,把element的stack指向我们的stack,并且把值付给stack的element。
然后看这个:
if (srcEnd == LINK_CAPACITY && head.next != null) {
// Add capacity back as the Link is GCed.
reclaimSpace(LINK_CAPACITY);
this.head = head.next;
}
我们的srcEnd==LINK_CAPACITY那就说明link都回收完了,并且head.next!=null那就说明还有下一个link,那么我们就把link的这个位置腾出来,也就是调用reclaimSpce(LINK_CAPACITY):
private void reclaimSpace(int space) {
assert space >= 0;
availableSharedCapacity.addAndGet(space);
}
太简单就是把之前使用的空间重新拿出来,availableSharedCapacity又变大了。
再看这个源码:
head.readIndex = srcEnd;
把head.readIndex赋值为srcEnd,下次我们又可以从src开始获取对象了。
再看:
if (dst.size == newDstSize) {
return false;
}
这段代码就是说我们newDstSize和之前dst.size和一样,表明我们其实没有回收到对象,那就返回false了。
否则,说明回收到了对象,把dist.size重新赋值,并且返回true:
dst.size = newDstSize;
return true;
最后一个return false表明stack满了,因为srcEnd最终还是和srcStart相同,说明这个地方没有扩容成功:
if (expectedCapacity > dst.elements.length) {
final int actualCapacity = dst.increaseCapacity(expectedCapacity);
srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
}
没有扩容成功才会导致srcEnd=srcStart。
这个transfer就是尽量把我们当前的link里面的对象传输到stack那里,成功返回true,否则返回false。
最后我们总结一下异线程回收和获取对象,假设A线程创建了对象,B线程回收对象,B线程回收的时候回放到A线程对应的stack对应的WeakOrderQueue里面,A线程获取的时候就直接从这个WeakOrderQueue里面取就可以了。