Netty对象池 Recycler

Netty对象池 Recycler

对于为什么要使用对象池,肯定是提高性能啊!
对象的创建,回收,再创建;很耗损jvm性能的,所以就有了对象池的概念,Netty通过Recycler管理对象的创建与回收,而在对象回收时,也不是真正的把对象从堆内存中移除了,而是在内存中缓存了,那具体什么时候回收呢?对象有一个WeakReference,GC回收就与弱引用有关了。具体请查看:强引用、软引用、弱引用、虚引用他们之间的区别了。

一、对象池组件介绍

1、Recycler

Recycler是对所有组件的一个管理,通过源码可以看出,它的方法并不多,只对外暴露提供get()recycle()方法,get()方法是从对象池中获取“对象”,recycle()是把“对象”进行回收;Recycler还提供了公用“对象”DELAYED_RECYCLED,具体作用将在下面介绍逻辑时详细解释。

2、Stack

Stack是对象池的最直接的管理类,提供了对象创建、回收、异线程“对象”获取、对象移除等。Stack持有指向上层Recycler的指针。

3、DefaultHandle

DefaultHandle是实际存储缓存“对象”的类,它有一个Object value属性,就是用来保存实际“对象”,另外DefaultHandle还有指向上层Stack的指针,代表该DefaultHandle实际属于哪个Stack

4、WeakOrderQueue

WeakOrderQueue是一个单向队列,它主要用来存储共享数据(对象池中的对象),在Stack中未get到"对象"时,将从该队列中检其他线程创建的"对象"。并把检索到的所有“对象”复制到本Stack中;对异线程的“对象”回收,也会放到WeakOrderQueue中。

5、Head

WeakOrderQueue的内部类,每个Head都会有一个Link链表,下面会讲解Link的作用。Head有一个availableSharedCapacity属性,该属性与Stack的availableSharedCapacity绑定,可以有Head动态扩展分享容量的大小。所以Head的作用是有一个Link的指针和动态扩展容量。

6、Link

每个Link都有DefaultHandle<?>[] elements属性,这是实际存储“对象”数据的地方,而跨线程检索“对象”,也就是从各个Link中检索,如果查到了,就整体复制到Stackelements中。Link继承AtomicInteger,所以Link还拥有AtomicInteger的一切特性。

二、组件关系

单从上面各个组件的介绍,无法实际理解他们之间的关系;下面就根据下图整体介绍一下:
Recycler组件关系图
(1)每个Recycler都有一个Stack对象,用来实际存储本线程的“对象”数据。
(2)所有Recycler又有一个公共的FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED对象,该对象是静态的,所以数据在各个线程中共享,每个Recycler创建“对象”之后,都会在DELAYED_RECYCLED中缓存一份。从存储类型可以看出,Stack作为Key,WeakOrderQueue作为Value。而Stack又与Recycler绑定。
(3)Recycler还有一个FastThreadLocal<Stack> threadLocal属性,用来存储该Recycler关联的Stack对象的。
(4)Stack中有DefaultHandle<?>[] elements属性,实际“对象”数据存储的地方。
(5)Stack有 volatile WeakOrderQueue head属性,是多线程立即可见的。
(6)WeakOrderQueue中又Head head属性
(7)Head有Link属性。
(8)Link是单向链表。
通过描述和上面的结构图,应该很清楚各个组件之间的关系了。

三、实现对象缓存、获取与检索

该过程会设计到很多源码分析,一步一步理解之后详细看下去,肯定能弄明白,不要着急。

1、下面是Recycler的实际使用,大家在学习是可以打断点一点一点的追查下去。
import io.netty.util.Recycler;

public class RecyclerDemo {
    
    
    // 创建内存池,实现内存池的newObject方法
    public static final Recycler<Worker> recycler = new Recycler<Worker>() {
    
    
        @Override
        protected Worker newObject(Handle<Worker> handle) {
    
    
            return new Worker(handle); // 此处传入handle用来对象回收
        }
    };

    public static void main(String[] args) {
    
    
        Worker worker1 = recycler.get(); // 从对象池中获取Worker对象,如果获取不到,并且handle也为空,则会调用上面的newObject创建一个对象
        worker1.setName("1111");
        worker1.recycle(); // 对象进行回收,实际回收并不会把对象从内存中删除,而是又放回了对象池
        Worker worker2 = recycler.get();
        System.out.println(worker2.getName());
        System.out.println(worker1 == worker2);
    }

    public static class Worker {
    
    
        private String name;
        private Recycler.Handle<Worker> handle;
        public Worker(Recycler.Handle<Worker> handle) {
    
     this.handle = handle; }
        public String getName() {
    
     return name; }
        public void setName(String name) {
    
     this.name = name; }
        // 实际实现对象回收的代码
        public void recycle() {
    
     handle.recycle(this); }
    }
}

上面代码输出结果如下:说明两次从对象池中获取的对象是同一个。

1111
true

2、分析recycler.get() 对象如何创建的?

下面是Recycler的get方法源码

public final T get() {
    
    
    // 保证该线程的最大容量不为0,否则就返回一个空对象
    if (maxCapacityPerThread == 0) {
    
    
        return newObject((Handle<T>) NOOP_HANDLE);
    }
    // 从threadLocal获取缓存的Stack对象
    Stack<T> stack = threadLocal.get();
    // 从stack中弹出一个最新创建的“对象”
    DefaultHandle<T> handle = stack.pop();
    // 如果为空,则创建
    if (handle == null) {
    
    
        handle = stack.newHandle();
        // 此处的newObject就是在Demo中重写的newObject
        handle.value = newObject(handle); 
    }
    return (T) handle.value;
}
 // 创建DefaultHandle对象,应对上面方法
DefaultHandle<T> newHandle() {
    
    
    return new DefaultHandle<T>(this);
}

对象至此创建完成。

3、threadLocal属性什么时候赋值的 stack对象什么时候存储到threadLocal中的?

由此可见,在new Recycler的时候,就把threadLocal也一并创建了,又重写了initialVale()和onRemoval()方法。
stack对象创建就是在initialVale()中,原理与Recycler创建Worker对象相同。

private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
    
    
    @Override
    protected Stack<T> initialValue() {
    
    
        return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
                ratioMask, maxDelayedQueuesPerThread);
    }

    @Override
    protected void onRemoval(Stack<T> value) {
    
    
        // Let us remove the WeakOrderQueue from the WeakHashMap directly if its safe to remove some overhead
        if (value.threadRef.get() == Thread.currentThread()) {
    
    
           if (DELAYED_RECYCLED.isSet()) {
    
    
               DELAYED_RECYCLED.get().remove(value);
           }
        }
    }
};
4、重点来了,stack.pop()
DefaultHandle<T> pop() {
    
    
    int size = this.size; // @1 第一个重点
    if (size == 0) {
    
     
        if (!scavenge()) {
    
     // @2 第二个重点
            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;
}

(1)在@1标注的this.size表示当前线程(ThreadA)存储“对象”的数量。如果数量为0,则从别的线程(ThreadB:此处只列举一个线程)中检索。
(2)@2标注的代码就是检索逻辑:scavenge(),scavenge()中只有scavengeSome()是有用的。

boolean scavenge() {
    
    
    // continue an existing scavenge, if any
    if (scavengeSome()) {
    
    
        return true;
    }
    // reset our scavenge cursor
    prev = null;
    cursor = head;
    return false;
}
5、分析scavengeSome()方法
boolean scavengeSome() {
    
    
    WeakOrderQueue prev;
    WeakOrderQueue cursor = this.cursor; // @1 start
    if (cursor == null) {
    
    
        prev = null;
        cursor = head;
        if (cursor == null) {
    
    
            return false;
        }
    } else {
    
    
        prev = this.prev;
    } // @1 end
    boolean success = false;
    do {
    
    
        if (cursor.transfer(this)) {
    
     // @2
            success = true;
            break;
        }
        WeakOrderQueue next = cursor.next;  // @3
        if (cursor.owner.get() == null) {
    
    
            if (cursor.hasFinalData()) {
    
    
                for (;;) {
    
    
                    if (cursor.transfer(this)) {
    
    
                        success = true;
                    } else {
    
    
                        break;
                    }
                }
            }
            if (prev != null) {
    
    
                prev.setNext(next);
            }
        } else {
    
    
            prev = cursor;
        }
        cursor = next;
    } while (cursor != null && !success);
    this.prev = prev;
    this.cursor = cursor;
    return success;
}

(1)@1start到@1end,拿到cursor的游标,从cursor处开始查找。
(2)根据WeakOrderQueue的单向链表特性,一个节点一个节点的往后查找。而实际操作查找的方法是transfer;而scavengeSome()就是个控制指针移动的方法。

6、分析transfer()
boolean transfer(Stack<?> dst) {
    
    
    Link head = this.head.link; // @1
    if (head == null) {
    
    
        return false;
    }

    if (head.readIndex == LINK_CAPACITY) {
    
     // @2
        if (head.next == null) {
    
    
            return false;
        }
        this.head.link = head = head.next;
        this.head.reclaimSpace(LINK_CAPACITY); // @2end
    }

    final int srcStart = head.readIndex; // @3 
    int srcEnd = head.get();
    final int srcSize = srcEnd - srcStart;
    if (srcSize == 0) {
    
    
        return false;
    } // @3end

    final int dstSize = dst.size; // @4
    final int expectedCapacity = dstSize + srcSize;

    if (expectedCapacity > dst.elements.length) {
    
    
        final int actualCapacity = dst.increaseCapacity(expectedCapacity);
        srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
    } // @4end

    if (srcStart != srcEnd) {
    
     // @5
        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;
        } // @5end

        if (srcEnd == LINK_CAPACITY && head.next != null) {
    
     // @6
            // Add capacity back as the Link is GCed.
            this.head.reclaimSpace(LINK_CAPACITY);
            this.head.link = 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;
    }
}

(1) @1的作用是拿到要检索的WeakOrderQueue中的Head指向的Link;WeakOrderQueue中有Head,Head中又Link,Link中又DefaultHandle数组,该数组是存储“对象”的;
(2)@2的作用是判断Link有没有读到尾部,如果已经读到了尾部则把下一个Link拿出来;
(3)Link是继承AtomicInteger,@3的作用就是拿到Link中的可读的下标起始和终点值。
(4)@4是拿到数据最终拷贝目标地址,并保证目标地址的内存足够拷贝数据;
(5)@5执行拷贝,并过滤中间已经被删除的对象。

四、对象回收 recycle()

回收方法如下:最终真正执行的是stack.push();

public final boolean recycle(T o, Handle<T> handle) {
    
    
    if (handle == NOOP_HANDLE) {
    
    
        return false;
    }
    DefaultHandle<T> h = (DefaultHandle<T>) handle;
    if (h.stack.parent != this) {
    
    
        return false;
    }
    h.recycle(o);
    return true;
}
public void recycle(Object object) {
    
    
    Stack<?> stack = this.stack;
    if (lastRecycledId != recycleId || stack == null) {
    
    
        throw new IllegalStateException("recycled already");
    }
    stack.push(this);
}

Thread currentThread = Thread.currentThread();
if (threadRef.get() == currentThread) {
    
    
    pushNow(item);
} else {
    
    
    pushLater(item, currentThread);
}

通过上面最后一段代码,如果执行回收的是本线程的对象,则执行pushNow();否则执行pushLater().

1、本线程内的对象回收: pushNow()
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)) {
    
    
        return;
    }
    if (size == elements.length) {
    
    
        elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
    }
    elements[size] = item;
    this.size = size + 1;
}

这段代码简单,就是把要回收的对象再放回到Stack的elements中,另外再加上一些空间的验证。

2、异线程的对象回收: pushLater()
private void pushLater(DefaultHandle<?> item, Thread thread) {
    
    
    Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
    WeakOrderQueue queue = delayedRecycled.get(this);
    if (queue == null) {
    
    
        if (delayedRecycled.size() >= maxDelayedQueues) {
    
    
            delayedRecycled.put(this, WeakOrderQueue.DUMMY);
            return;
        }
        if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
    
    
            return;
        }
        delayedRecycled.put(this, queue);
    } else if (queue == WeakOrderQueue.DUMMY) {
    
    
        return;
    }

    queue.add(item);
}

从DELAYED_RECYCLED中获取该线程对应的WeakOrderQueue,如果为null,则创建一个WeakOrderQueue对象,不为空则之间添加到queue中。

至此整个对象池的解析完成。
纯属原著,一点一点分析的Netty对象池源码,如有转载,请声明!

猜你喜欢

转载自blog.csdn.net/Kelvin_hui/article/details/114390334
今日推荐