netty源码阅读之性能优化工具类之Recycle异线程获取对象

在这篇《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里面取就可以了。

猜你喜欢

转载自blog.csdn.net/fst438060684/article/details/82971452