Netty ObjectPool对象池技术原理分析

一、ObjectPool使用示例

1.对需要使用对象池的对象,定义一个ObjectPool的静态全局变量RECYCLE,用于对象的分配和回收。并在对象内定义一个ObjectPool.Handle成员变量,并且将此变量作为构造函数参数传入,并将构造函数作为私有。然后添加一个回收的方法Recycle,在不需要此对象时调用handle.recycle()

获取对象则调用ObjectPool.get

@Slf4j
public class ObjectRecycleTest {

    private static ExecutorService executor = new ThreadPoolExecutor(3, 6, 10,
            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), new NamedThreadFactory("local", false));

    public static final class People {
        String name;
        int age;
        boolean sex;

        private ObjectPool.Handle<People> handle;

        private static final ObjectPool<People> RECYCLE = ObjectPool.newPool((handle) -> {
            return new People(handle);
        });

        private People(ObjectPool.Handle<People> handle) {
            this.handle = handle;
        }

        public static People newInstance(String name, int age, boolean sex) {
            People people = RECYCLE.get();
            people.age = age;
            people.name = name;
            people.sex = sex;
            return people;
        }

        public void recycle() {
            name = "";
            age = 0;
            handle.recycle(this);
        }
    }

    public void testRecycle() {
        Set<People> peopleList = new HashSet<People>();
        for (int i = 0; i < 5; i++) {
            People p1 = People.newInstance("jack", 28, i % 2 == 0);
//            log.debug(" p1:{}",p1);
            peopleList.add(p1);
        }

        peopleList.stream().forEach(t -> {
                    if (t.sex) {
                        t.recycle();
                    }
                }
        );

        try {
            executor.submit(() -> {
                peopleList.stream().forEach(t -> {
                            if (!t.sex) {
                                t.recycle();
                            }
                        }
                );
            }).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 2; i++) {
            People p2 = People.newInstance("jack", 28, i % 2 == 0);
            if (peopleList.contains(p2)) {
                log.debug(" in exist p2:{}", p2);
            } else {
//                log.debug(" new instance p2:{}",p2);
            }
        }
        executor.submit(() -> {
            People p3 = People.newInstance("qiuye", 26, true);
            log.debug(" p3:{}", p3);
        });
    }

    public static void main(String[] args) {
        ObjectRecycleTest objectRecycleTest = new ObjectRecycleTest();
        objectRecycleTest.testRecycle();
    }
}

二、实例对象创建流程

1.调用ObjectPool.get来获取实例,这是对Recycler的一个封装。

   

public abstract class ObjectPool<T> {

    ObjectPool() { }

    public static <T> ObjectPool<T> newPool(final ObjectCreator<T> creator) {
        return new RecyclerObjectPool<T>(ObjectUtil.checkNotNull(creator, "creator"));
    }

    private static final class RecyclerObjectPool<T> extends ObjectPool<T> {
        private final Recycler<T> recycler;

        RecyclerObjectPool(final ObjectCreator<T> creator) {
             recycler = new Recycler<T>() {
                @Override
                protected T newObject(Handle<T> handle) {
                    return creator.newObject(handle);
                }
            };
        }

        @Override
        public T get() {
            return recycler.get();
        }
    }
}

2.Recycler的类中有一个threadLocal的stack类型的栈对象(保证每个线程数据私有和安全),这里面保存有所有曾经回收到准备重利用的对象。

public abstract class Recycler<T> {
 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,
                    interval, maxDelayedQueuesPerThread, delayedQueueInterval);
        }

        @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);
               }
            }
        }
    };

3.Recycler.get方法,就是从stack中获取对象,获取为空,则新建一个对象和Handle,并将Handle与Stack关联。

    public final T get() {
        if (maxCapacityPerThread == 0) {
            return newObject((Handle<T>) NOOP_HANDLE);
        }
        Stack<T> stack = threadLocal.get();
        DefaultHandle<T> handle = stack.pop();
        if (handle == null) {
            handle = stack.newHandle();
            handle.value = newObject(handle);
        }
        return (T) handle.value;
    }

4.stack.pop方法,则是获取历史的回收的对象。

private static final class Stack<T> {
DefaultHandle<?>[] elements;
private volatile WeakOrderQueue head;
DefaultHandle<T> pop() {
            int size = this.size;
            if (size == 0) {
                if (!scavenge()) {
                    return null;
                }
                size = this.size;
                if (size <= 0) {
                    // double check, avoid races
                    return null;
                }
            }
            size --;
            DefaultHandle ret = elements[size];
            elements[size] = null;
            this.size = size;

            if (ret.lastRecycledId != ret.recycleId) {
                throw new IllegalStateException("recycled multiple times");
            }
            ret.recycleId = 0;
            ret.lastRecycledId = 0;
            return ret;
        }

这里有两种对象来源。

     (1).elements数组是保存新建对象和回收对象在同一个线程的回收对象。

    (2).类型WeakOrderQueue的head对象则是一个弱引用链接队列,这个队列用来保存所有创建和回收线程不一致的回收对象。

     (3).首先从elements获取回收对象,如果这里不为空,则返回第一个,总数减1.

    (4).如果elements为空,则将head队列中的回收对象复制到elements,再次获取。

5.我们首先看一下WeakOrderQueue的数据结构

这里面有两个next属性

 (1).本身的next成员变量,是指向下一个线程回收的WeakOrderQueue,WeakOrderQueue这个在一个stack中也是多个,是一个单身链接。

(2).本身有个head,tail节点,用来指向对象内部的Link对象队列。插入数据从tail进行,弹出数据从head进行。

  private static final class WeakOrderQueue extends WeakReference<Thread> {

        static final WeakOrderQueue DUMMY = new WeakOrderQueue();

        @SuppressWarnings("serial")
        static final class Link extends AtomicInteger {
            final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];

            int readIndex;
            Link next;
        }


        private static final class Head {
            private final AtomicInteger availableSharedCapacity;

            Link link;

            Head(AtomicInteger availableSharedCapacity) {
                this.availableSharedCapacity = availableSharedCapacity;
            }

            private void reclaimSpace(int space) {
                availableSharedCapacity.addAndGet(space);
            }

            void relink(Link link) {
                reclaimSpace(LINK_CAPACITY);
                this.link = link;
            }
        }

        // chain of data items
        private final Head head;
        private Link tail;
        // pointer to another queue of delayed items for the same stack
        private WeakOrderQueue next;
        private final int id = ID_GENERATOR.getAndIncrement();
        private final int interval;
        private int handleRecycleCount;

5.从head队列复制回收对象列表到elements的流程, 这里先找一个可用的不为空的队列,并且将回收对象(这里的回收对象都为Handle类型)列表 复制到elements数组。

  private boolean scavengeSome() {
            WeakOrderQueue prev;
            WeakOrderQueue cursor = this.cursor;
            if (cursor == null) {
                prev = null;
                cursor = head;
                if (cursor == null) {
                    return false;
                }
            } else {
                prev = this.prev;
            }

            boolean success = false;
            do {
                if (cursor.transfer(this)) {
                    success = true;
                    break;
                }
                WeakOrderQueue next = cursor.getNext();
                if (cursor.get() == null) {
                    if (cursor.hasFinalData()) {
                        for (;;) {
                            if (cursor.transfer(this)) {
                                success = true;
                            } else {
                                break;
                            }
                        }
                    }

                    if (prev != null) {
                        // Ensure we reclaim all space before dropping the WeakOrderQueue to be GC'ed.
                        cursor.reclaimAllSpaceAndUnlink();
                        prev.setNext(next);
                    }
                } else {
                    prev = cursor;
                }

                cursor = next;

            } while (cursor != null && !success);

            this.prev = prev;
            this.cursor = cursor;
            return success;
        }

6.具体的从WeakOrderQueue复制数据到stack,是由WeakOrderQueue的transfer完成。

    boolean transfer(Stack<?> dst) {
            Link head = this.head.link;
            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;
                }
           
                head.readIndex = srcEnd;
                dst.size = newDstSize;
                return true;
            } else {
                // The destination stack is full already.
                return false;
            }
        }

这里面的代码比较长,也就是从head.link节点开始,复制handle对象数据到elements中。这里特殊要注意一下,这种跨线程的对象,也会采取丢失策略。即丢7取1.即stack.drophandle,到时在回收对象时讲述。

7.这里再回到第4步,由于stack.elements有数据了 ,所以跟之前的流程一样。从这里可以看出,同线程回收的对象,都可以重复使用,跨线程回收的对象,只能丢7取1.

三、实例对象回收过程。

1.从handle.recycle开始,handle保存在People实例对象的属性中。

  private static final class DefaultHandle<T> implements Recycler.Handle<T> {
        int lastRecycledId;
        int recycleId;
        boolean hasBeenRecycled;
        Recycler.Stack<?> stack;
        Object value;

        DefaultHandle(Recycler.Stack<?> stack) {
            this.stack = stack;
        }

        public void recycle(Object object) {
            if (object != this.value) {
                throw new IllegalArgumentException("object does not belong to handle");
            } else {
                Recycler.Stack<?> stack = this.stack;
                if (this.lastRecycledId == this.recycleId && stack != null) {
                    stack.push(this);
                } 
            }
        }
    }

2.这里会调用stack.push(this),stack即为创建时当前线程所绑定的threadlocal对象。

Stack       
 void push(Recycler.DefaultHandle<?> item) {
            Thread currentThread = Thread.currentThread();
            if (this.threadRef.get() == currentThread) {
                this.pushNow(item);
            } else {
                this.pushLater(item, currentThread);
            }

        }

3.判断如果是当前线程,则pushNow,否则pushLater,pushNow就是放到elements数组,pushLater就是放到WeakOrderQueue类型的head队列。

我们先看pushNow

       private void pushNow(Recycler.DefaultHandle<?> item) {
            if ((item.recycleId | item.lastRecycledId) != 0) {
                throw new IllegalStateException("recycled already");
            } else {
                item.recycleId = item.lastRecycledId = Recycler.OWN_THREAD_ID;
                int size = this.size;
                if (size < this.maxCapacity && !this.dropHandle(item)) {
                    if (size == this.elements.length) {
                        this.elements = (Recycler.DefaultHandle[])Arrays.copyOf(this.elements, Math.min(size << 1, this.maxCapacity));
                    }

                    this.elements[size] = item;
                    this.size = size + 1;
                }
            }
        }

这个比较简单,就是放到elements数组,并且 SIZE自增,但是要注意的是会进行一次dropHandle过滤,8个对象只有一个会存下来,其它的都会被GC回收,不能重复使用。

        boolean dropHandle(Recycler.DefaultHandle<?> handle) {
            if (!handle.hasBeenRecycled) {
                if (this.handleRecycleCount < this.interval) {
                    ++this.handleRecycleCount;
                    return true;
                }

                this.handleRecycleCount = 0;
                handle.hasBeenRecycled = true;
            }

            return false;
        }

4.接下来我们看pushLater, 这里面会判断当前stack对象是否在当前回收线程的

private static final FastThreadLocal<Map<Recycler.Stack<?>, Recycler.WeakOrderQueue>> DELAYED_RECYCLED这个LOCAL线程数据的Map中是否存在(一个线程可以有多个stack,多个对象回收器)。如果不存在,则创建一个newWeakOrderQueue,然后将当前handle保存到WeakOrderQueue中。
   private void pushLater(Recycler.DefaultHandle<?> item, Thread thread) {
            if (this.maxDelayedQueues != 0) {
                Map<Recycler.Stack<?>, Recycler.WeakOrderQueue> delayedRecycled = (Map)Recycler.DELAYED_RECYCLED.get();
                Recycler.WeakOrderQueue queue = (Recycler.WeakOrderQueue)delayedRecycled.get(this);
                if (queue == null) {
                    if (delayedRecycled.size() >= this.maxDelayedQueues) {
                        delayedRecycled.put(this, Recycler.WeakOrderQueue.DUMMY);
                        return;
                    }

                    if ((queue = this.newWeakOrderQueue(thread)) == null) {
                        return;
                    }

                    delayedRecycled.put(this, queue);
                } else if (queue == Recycler.WeakOrderQueue.DUMMY) {
                    return;
                }

                queue.add(item);
            }
        }

5.新建队列,这里可以看到新建一个head,tail,并且head.link指向第一个tail,

        static Recycler.WeakOrderQueue newQueue(Recycler.Stack<?> stack, Thread thread) {
            if (!Recycler.WeakOrderQueue.Head.reserveSpaceForLink(stack.availableSharedCapacity)) {
                return null;
            } else {
                Recycler.WeakOrderQueue queue = new Recycler.WeakOrderQueue(stack, thread);
                stack.setHead(queue);
                return queue;
            }
        }

        private WeakOrderQueue(Recycler.Stack<?> stack, Thread thread) {
            super(thread);
            this.id = Recycler.ID_GENERATOR.getAndIncrement();
            this.tail = new Recycler.WeakOrderQueue.Link();
            this.head = new Recycler.WeakOrderQueue.Head(stack.availableSharedCapacity);
            this.head.link = this.tail;
            this.interval = stack.delayedQueueInterval;
            this.handleRecycleCount = this.interval;
        }

6.入队过程,就是放到tail.elements数组中,因为tail为Link类型,这个类又继承于

AtomicInteger,所以巧妙的运行AtomicInteger.value来标志为写位置。这个有个要注意,如果tail中的elements已满,则新建一个Link作为新的tail,老的tail的NEXT指向新tail节点,而head.link则一直指向第一个tail,构成一个单向链接。
        void add(Recycler.DefaultHandle<?> handle) {
            handle.lastRecycledId = this.id;
            if (this.handleRecycleCount < this.interval) {
                ++this.handleRecycleCount;
            } else {
                this.handleRecycleCount = 0;
                Recycler.WeakOrderQueue.Link tail = this.tail;
                int writeIndex;
                if ((writeIndex = tail.get()) == Recycler.LINK_CAPACITY) {
                    Recycler.WeakOrderQueue.Link link = this.head.newLink();
                    if (link == null) {
                        return;
                    }

                    this.tail = tail = tail.next = link;
                    writeIndex = tail.get();
                }

                tail.elements[writeIndex] = handle;
                handle.stack = null;
                tail.lazySet(writeIndex + 1);
            }
        }

7.查看堆栈快照数据,先他截6个对象,然后当前线程回收3个,其它线程回收3个。则是在stack的elements中有一个和WeakOrderQueue的head中有一个(丢7取1策略)

8.至此,分析完成,可以看到,对象池不是完全重复使用,而是丢7存1,防止内存溢出。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值