Netty源码(八)对象池

1,172 阅读12分钟

前言

本章学习Netty对象池。

  • 如何使用Netty对象池,实现对象的创建和回收
  • Netty如何抽象对象池
  • Netty如何实现对象池
  • 如何处理同一个对象在不同线程创建和回收的场景

一、如何使用Netty对象池

Item是一个普通对象,这个对象由对象池管理。

public static class Item {
    // 类变量,一个Item对象池
    private static final ObjectPool<Item> pool = ObjectPool.newPool(new ObjectPool.ObjectCreator<Item>() {
        @Override
        public Item newObject(ObjectPool.Handle<Item> handle) {
            return new Item(handle);
        }
    });
    // 普通的业务属性
    private int id;
    // 对象回收钩子
    private final ObjectPool.Handle<Item> handle;

    /**
    * 私有构造方法
    */
    private Item(ObjectPool.Handle<Item> handle) {
        this.handle = handle;
    }

    /**
    * 从对象池获取item
    */
    public static Item getInstance() {
        return pool.get();
    }

    /**
    * 回收
    */
    public void recycle() {
        this.handle.recycle(this);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

接着使用对象池创建对象和回收对象。

@Test
public void test() {
    Item item01 = Item.getInstance(); // 池中没有对象缓存,创建了一个新的对象
    item01.setId(1);
    System.out.println(item01); // 5c7fa833
    item01.recycle(); // 使用handle钩子回收item01
    item01 = Item.getInstance(); // 重新从池中获取一个对象
    System.out.println(item01); // 5c7fa833
    System.out.println(item01.getId()); // 1
}

二、对象池抽象结构

Netty针对对象池,设计了一套自己的抽象结构,包括对象池、对象创建、对象回收,这三个抽象定义都在ObjectPool类中。

1、对象池

ObjectPool抽象类,是对象池的顶层抽象,需要子类实现一个get方法。如果池内有对象,则直接通过get方法返回,否则需要通过ObjectCreator#newObject(Handle)方法创建一个新对象返回。

/**
 * Light-weight object pool.
 *
 * @param <T> the type of the pooled object
 */
public abstract class ObjectPool<T> {

    ObjectPool() { }

    /**
     * Get a {@link Object} from the {@link ObjectPool}. The returned {@link Object} may be created via
     * {@link ObjectCreator#newObject(Handle)} if no pooled {@link Object} is ready to be reused.
     */
    public abstract T get();
}

2、对象创建

ObjectCreator是对象创建的抽象接口,需要实现一个newObject方法,传入Handle,创建一个新对象,之后这个对象可以被Handle#recycle回收。言外之意,对象需要持有Handle,在未来某一时刻通过Handle可以回收当前实例到ObjectPool。

/**
* Creates a new Object which references the given {@link Handle} and calls {@link Handle#recycle(Object)} once
* it can be re-used.
*
* @param <T> the type of the pooled object
*/
public interface ObjectCreator<T> {

    /**
    * Creates an returns a new {@link Object} that can be used and later recycled via
    * {@link Handle#recycle(Object)}.
    */
    T newObject(Handle<T> handle);
}

3、对象回收

Handle会在ObjectCreator创建对象时传入,客户端需要通过操作Handle的recycle方法回收对象。

/**
* Handle for an pooled {@link Object} that will be used to notify the {@link ObjectPool} once it can
* reuse the pooled {@link Object} again.
* @param <T>
*/
public interface Handle<T> {
    /**
    * Recycle the {@link Object} if possible and so make it ready to be reused.
    */
    void recycle(T self);
}

三、Netty实现对象池

Netty针对上面的这套抽象接口,实现了自己的一个轻量级对象池,这个轻量级对象池的实现是ObjectPool的静态内部类RecyclerObjectPool

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

可以看到,创建一个RecyclerObjectPool,需要传入ObjectCreator接口的实现类,此外不需要提供其他方法。而RecyclerObjectPool内部其实是创建了一个Recycler,Recycler借助ObjectCreator可以在池内无缓存对象时创建对象,全部委托Recycler实现了对象池。

此外Netty在ObjectPool提供了一个静态方法,用于创建RecyclerObjectPool。

/**
* Creates a new {@link ObjectPool} which will use the given {@link ObjectCreator} to create the {@link Object}
* that should be pooled.
*/
public static <T> ObjectPool<T> newPool(final ObjectCreator<T> creator) {
    return new RecyclerObjectPool<T>(ObjectUtil.checkNotNull(creator, "creator"));
}

四、Recycler

Recycler是对象池获取对象的入口,先来看看类变量。

首先最关键的是一个FastThreadLocal实例threadLocal,每个线程管理一个Stack实例,这个Stack是真正的池

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) {
        if (value.threadRef.get() == Thread.currentThread()) {
            if (DELAYED_RECYCLED.isSet()) {
                DELAYED_RECYCLED.get().remove(value);
            }
        }
    }
};

另外,考虑到对象的创建和回收可能不在同一个线程中完成,对象池就变得复杂了。这里还有一个FastThreadLocal实例DELAYED_RECYCLED,保存了当前线程stack帮助其他线程stack回收的容器WeakOrderQueue。言外之意,如线程A的Stack分配了实例X,线程B的Stack回收了实例X,这个FastThreadLocal针对线程B而言,Map的key是线程A的Stack,value是线程B创建的WeakOrderQueue容器,用于存储帮助线程A的Stack回收的对象。

// 其他线程的stack实例 - 线程帮助其他线程的stack回收的对象放在WeakOrderQueue容器中
private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
    new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
    @Override
    protected Map<Stack<?>, WeakOrderQueue> initialValue() {
        return new WeakHashMap<Stack<?>, WeakOrderQueue>();
    }
};

Recycler的成员变量都是为了约束对象池的大小,防止对象池膨胀。

private final int maxCapacityPerThread; // 4096
private final int maxSharedCapacityFactor; // 2
private final int interval; // 8
private final int maxDelayedQueuesPerThread; // 核数*2
private final int delayedQueueInterval; // 8
  • maxCapacityPerThread:由于对象池是基于FastThreadLocal实现的线程局部缓存,为了防止缓存过多实例,maxCapacityPerThread是控制一个Stack可以存放多少缓存实例。
  • maxSharedCapacityFactor:一个因子,用于计算其他线程帮助当前线程回收实例个数的上限。
  • interval/delayedQueueInterval:为了防止对象池膨胀,并不是每次回收对象都会重新放入对象池,对于后面介绍的各类容器,一般是每8次才会真正缓存一次。
  • maxDelayedQueuesPerThread:每个线程最多能帮助核数*2个线程回收。

Recycler的get方法是对象池的入口方法。借助threadlocal获取当前线程对应的Stack实例,通过操作Stack得到Handle和Handle对应的value(客户端对象实例)。

public final T get() {
    // 默认4096
    if (maxCapacityPerThread == 0) {
        return newObject((Handle<T>) NOOP_HANDLE);
    }
    // 获取当前线程的Stack
    Stack<T> stack = threadLocal.get();
    // 获取Handle
    DefaultHandle<T> handle = stack.pop();
    // 如果stack中有Handle,直接返回缓存的对象,否则创建Handle
    if (handle == null) {
        // 创建DefaultHandle 等价于 handle = new DefaultHandle<>(stack)
        handle = stack.newHandle();
        // 调用RecycleObjectPool构造方法中的匿名类的newObject方法,实际上是客户端实现的匿名ObjectCreator
        handle.value = newObject(handle);
    }
    return (T) handle.value;
}

五、DefaultHandle

DefaultHandle是Netty对象池给客户端的一个回收对象的钩子(见第一节的案例)。

private static final class DefaultHandle<T> implements Handle<T> {
    int lastRecycledId;
    int recycleId;
    boolean hasBeenRecycled;
    Stack<?> stack;
    Object value;
    DefaultHandle(Stack<?> stack) {
        this.stack = stack;
    }
    @Override
    public void recycle(Object object) {
        if (object != value) {
            throw new IllegalArgumentException("object does not belong to handle");
        }
        Stack<?> stack = this.stack;
        if (lastRecycledId != recycleId || stack == null) {
            throw new IllegalStateException("recycled already");
        }
        stack.push(this);
    }
}

成员变量value是客户端创建的对象,由对象池管理。recycle方法由客户端调用,将DefaultHandle放入Stack。

六、Stack

对象池.png

先对照Stack图例和成员变量大致了解一下它的结构。

private static final class Stack<T> {
    // Recycler属于一个RecyclerObjectPool,可以认为stack属于某个对象池实例
    final Recycler<T> parent;
    // userModel -> handle -> stack -> thread
    final WeakReference<Thread> threadRef;
    // 其他线程回收当前线程创建的对象的个数上限 = max(4096/2, 16)
    final AtomicInteger availableSharedCapacity;
    // 允许x个线程帮忙回收(WeakOrderQueue的最大个数)= 核数 * 2
    private final int maxDelayedQueues;
    // stack可以存储的DefaultHandle数量上限
    // 相当于当前线程可以存储的DefaultHandle上限,因为Stack都是由ThreadLocal控制的
    private final int maxCapacity;
    // handle回收的循环计数器,遇interval(8)变0
    private int handleRecycleCount;
    // 默认为8,每次尝试回收handle都会通过handleRecycleCount计数,当handleRecycleCount<8会丢弃
    private final int interval;
    // 和interval类似,但是是异线程回收到WeakOrderQueue时的阈值,默认也是8
    private final int delayedQueueInterval;
    // 存储DefaultHandle
    DefaultHandle<?>[] elements;
    // elements里实际元素的个数
    int size;
    // 由当前stack创建的对象,其他线程回收,这些实例保存在WeakOrderQueue中
    private WeakOrderQueue cursor, prev; // 消费WeakOrderQueue进度 双指针
    private volatile WeakOrderQueue head; // 存储WeakOrderQueue的头节点
}

大多数成员变量和Recycler一样,为了防止对象池膨胀由Recycler传入。重点关注几个成员变量:

  • elements:当前线程(Stack实例是ThreadLocal,可以认为是当前线程)缓存的Handle实例数组。初始容量256。
  • head:WeakOrderQueue组成的链表,存储其他线程帮助当前线程回收的Handle实例。
  • cursor/prev:当前Stack从WeakOrderQueue链表消费获取Handle的进度,这里是双指针。
  • threadRef:由于Stack是基于ThreadLocal操作的,这里存储的是对应线程的虚引用。使用虚引用的原因是:假设当前Stack对应ThreadA,ThreadB客户端持有ThreadA分配出去的对象,这个客户端对象持有Handle,Handle会持有Stack,Stack如果强引用ThreadA会导致ThreadA无法回收。
  • handleRecycleCount/interval:handleRecycleCount是一个循环计数器,最大值等于interval,初始值等于interval,每回收一个Handle加1。当handleRecycleCount小于interval时,Handle不会真的被放回池中,防止对象池膨胀。interval默认为8。

1、获取对象

pop方法用于从Stack中获取缓存的DefaultHandle。优先从当前线程回收的elements数组中取,如果elements数组为空,尝试从WeakOrderQueue中移动一些DefaultHandle到elements数组中,然后再从elements数组取。

DefaultHandle<T> pop() {
    int size = this.size;
    // 当前线程没有缓存Handle,elements数组中没有元素
    if (size == 0) {
        // 尝试从WeakOrderQueue中获取
        if (!scavenge()) {
            return null;
        }
        size = this.size;
        if (size <= 0) {
            return null;
        }
    }
    // 当前线程有缓存Handle,直接从elements数组中获取
    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;
}

如果elements数组为空,scavenge方法会尝试从cursor遍历WeakOrderQueue链表,调用WeakOrderQueue的transfer方法将其他线程帮助当前线程Stack回收的对象实例,放入当前Stack的elements数组中。

private boolean scavenge() {
    // 从cursor遍历WeakOrderQueue链表,尝试获取一些DefaultHandle放入elements数组
    if (scavengeSome()) {
        return true;
    }
    // 如果从WeakOrderQueue获取DefaultHandle失败,重置cursor和prev指针
    prev = null;
    cursor = head;
    return false;
}

private boolean scavengeSome() {
    WeakOrderQueue prev;
    WeakOrderQueue cursor = this.cursor;
    // cursor指针为空,初始化两个指针
    if (cursor == null) {
        prev = null;
        cursor = head;
        if (cursor == null) {
            return false;
        }
    } else {
        prev = this.prev;
    }
    boolean success = false;
    do {
        // 尝试从其他线程回收的WeakOrderQueue中获取handle到自己的elements中
        if (cursor.transfer(this)) {
            success = true;
            break;
        }
        WeakOrderQueue next = cursor.getNext();
        // 如果WeakOrderQueue引用的线程已经被回收,将这个WeakOrderQueue里的所有Handle移动到当前Stack的elements数组
        if (cursor.get() == null) {
            if (cursor.hasFinalData()) {
                for (;;) {
                    if (cursor.transfer(this)) {
                        success = true;
                    } else {
                        break;
                    }
                }
            }
            // 将cursor指向的被回收的线程的WeakOrderQueue从链表中移除
            if (prev != null) {
                cursor.reclaimAllSpaceAndUnlink();
                prev.setNext(next);
            }
        } else {
            prev = cursor;
        }
        cursor = next;
    } while (cursor != null && !success);
    this.prev = prev;
    this.cursor = cursor;
    return success;
}

2、归还对象

归还对象的入口是DefaultHandle的recycle方法,由客户端调用。比如PooledByteBuf抽象类,就使用到了对象池,当ByteBuf引用计数为0会触发deallocate方法,最后会调用DefaultHandle的recycle方法。因为PooledByteBuf并没有实际持有资源,只是盛放资源的容器对象(真正的资源是16MB的memory内存块),所以使用对象池可以减少new对象的开销和GC。

@Override
protected final void deallocate() {
    if (handle >= 0) {
        final long handle = this.handle;
        this.handle = -1;
        memory = null;
        chunk.arena.free(chunk, tmpNioBuf, handle, maxLength, cache);
        tmpNioBuf = null;
        chunk = null;
        recycle();
    }
}

private void recycle() {
    // DefaultHandle#recycle(PooledByteBuf)
    recyclerHandle.recycle(this);
}

DefaultHandle的recycle方法,调用Stack的push方法将DefaultHandle实例放入Stack。

void push(DefaultHandle<?> item) {
    Thread currentThread = Thread.currentThread();
    // 如果是当前线程创建的当前stack,且是当前线程在回收Handle,直接放入elements数组
    if (threadRef.get() == currentThread) {
        pushNow(item);
    }
    // 如果是其他线程创建的当前stack,但是由当前线程回收,放入WeakOrderQueue
    // 并将WeakOrderQueue插入其他线程的stack的WeakOrderQueue链表的头部
    else {
        pushLater(item, currentThread);
    }
}

pushNow方法将DefaultHandle直接放入当前stack的elements数组,但是有可能会丢弃这个Handle,防止对象池过度膨胀。

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;
    // 如果元素个数已经超过4096 或 dropHandle判断需要丢弃这个元素来控制对象池大小
    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;
}

/**
* 判断是否需要丢弃handle
*  构造时handleRecycleCount=interval=8
*  第一次不会被丢弃,2-8次会被丢弃,第九次不会被丢弃
*/
boolean dropHandle(DefaultHandle<?> handle) {
    if (!handle.hasBeenRecycled) {
        if (handleRecycleCount < interval) {
            handleRecycleCount++;
            // Drop the object.
            return true;
        }
        handleRecycleCount = 0;
        handle.hasBeenRecycled = true;
    }
    return false;
}

pushLater方法将DefaultHandle放入WeakOrderQueue,如果是新的WeakOrderQueue,会将WeakOrderQueue插入stack的WeakOrderQueue链表头部。WeakOrderQueue.DUMMY认为是一个空实现,并不能实际存放元素。当DELAYED_RECYCLED中当前线程帮助回收的stack超过16个(或认为是线程个数),就不会继续回收了。

private void pushLater(DefaultHandle<?> item, Thread thread) {
    Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
    WeakOrderQueue queue = delayedRecycled.get(this);
    if (queue == null) {
        // 如果当前线程已经帮16个线程回收了,不会再帮其他线程回收
        if (delayedRecycled.size() >= maxDelayedQueues) {
            delayedRecycled.put(this, WeakOrderQueue.DUMMY);
            return;
        }
        // 如果stack对应的WeakOrderQueue不存在,则创建并插入stack的WeakOrderQueue链表头部
        // 这里WeakOrderQueue也存在丢弃策略,所以可能返回空
        if ((queue = newWeakOrderQueue(thread)) == null) {
            return;
        }
        delayedRecycled.put(this, queue);
    } else if (queue == WeakOrderQueue.DUMMY) {
        return;
    }
    queue.add(item);
}

private WeakOrderQueue newWeakOrderQueue(Thread thread) {
    return WeakOrderQueue.newQueue(this, thread);
}

static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
    if (!Head.reserveSpaceForLink(stack.availableSharedCapacity)) {
        return null;
    }
    // 将stack和当前回收线程放入WeakOrderQueue
    final WeakOrderQueue queue = new WeakOrderQueue(stack, thread);
    // 插入stack的WeakOrderQueue链表,作为头节点
    stack.setHead(queue);
    return queue;
}

七、WeakOrderQueue

WeakOrderQueue继承WeakReference虚引执行回收的线程,内部用链表(Link)+数组(Link#elements)的方式存储协助回收线程回收的DefaultHandle。

private static final class WeakOrderQueue extends WeakReference<Thread> {
    // Link链表
    private final Head head;
    private Link tail;
    // 指向下一个Queue
    private WeakOrderQueue next;
    // 一个id标志
    private final int id = ID_GENERATOR.getAndIncrement();
    // 丢弃策略同Stack interval默认为8 handleRecycleCount初始为8 取值范围0-8
    private final int interval;
    private int handleRecycleCount;
    
    private WeakOrderQueue(Stack<?> stack, Thread thread) {
        super(thread);
        tail = new Link();
        head = new Head(stack.availableSharedCapacity);
        head.link = tail;
        interval = stack.delayedQueueInterval;
        handleRecycleCount = interval;
    }
    
    private static final class Head {
        // 最大容量 默认 2048 = Stack#availableSharedCapacity
        private final AtomicInteger availableSharedCapacity;
        // 实际头节点
        Link link;
    }
    
    static final class Link extends AtomicInteger {
        // 16个Handle
        final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];
        // AtomicInteger作为写index,是下一个可以插入elements的下标
        // readIndex作为读index,是下一个可以获取elements的下标
        int readIndex;
        // next指针
        Link next;
    }
}

其中Link有读写两个index,继承AtomicInteger作为写index,代表下次可插入elements数组的下标;readIndex代表下次可以获取的Handle位于elements数组的下标。

WeakOrderQueue的关键方法有两个,一个是add方法将Handle存储到Link中,一个是transfer方法选择一个Link中的所有Handle放入指定的Stack的elements中。

add方法逻辑比较简单,可以看到WeakOrderQueue和Link都有自己的丢弃策略,前者与Stack类似,后者是通过最大容量Stack#availableSharedCapacity限制的。

void add(DefaultHandle<?> handle) {
    handle.lastRecycledId = id;
    // Queue丢弃策略
    if (handleRecycleCount < interval) {
        handleRecycleCount++;
        return;
    }
    handleRecycleCount = 0;

    Link tail = this.tail;
    int writeIndex;
    // 如果尾Link已经满了,尝试创建新的Link接入
    if ((writeIndex = tail.get()) == LINK_CAPACITY) {
        // Link丢弃策略
        Link link = 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);
}

Link newLink() {
    return reserveSpaceForLink(availableSharedCapacity) ? new Link() : null;
}
/**
* 如果 availableSharedCapacity < LINK_CAPACITY(16),直接返回false,丢弃
* 否则 尝试 availableSharedCapacity = availableSharedCapacity - LINK_CAPACITY(16)
*/
static boolean reserveSpaceForLink(AtomicInteger availableSharedCapacity) {
    for (;;) {
        int available = availableSharedCapacity.get();
        if (available < LINK_CAPACITY) {
            return false;
        }
        if (availableSharedCapacity.compareAndSet(available, available - LINK_CAPACITY)) {
            return true;
        }
    }
}

transfer方法比较长,主要逻辑就是扩容+数据迁移。

boolean transfer(Stack<?> dst) {
    Link head = this.head.link;
    if (head == null) {
        return false;
    }

    if (head.readIndex == LINK_CAPACITY) {
        if (head.next == null) {
            return false;
        }
        head = head.next;
        this.head.relink(head);
    }

    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;
    // dst容量不足,倍乘扩容
    if (expectedCapacity > dst.elements.length) {
        final int actualCapacity = dst.increaseCapacity(expectedCapacity);
        srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
    }
    // 移动DefaultHandle到dst的elements中
    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;
			// 可能触发dst Stack的丢弃策略
            if (dst.dropHandle(element)) {
                // Drop the object.
                continue;
            }
            element.stack = dst;
            dstElems[newDstSize ++] = element;
        }

        if (srcEnd == LINK_CAPACITY && head.next != null) {
            this.head.relink(head.next);
        }

        head.readIndex = srcEnd;
        if (dst.size == newDstSize) {
            return false;
        }
        dst.size = newDstSize;
        return true;
    } else {
        return false;
    }
}

总结

  • 如何使用Netty对象池,实现对象的创建和回收?

    • 需要有一个构造方法,方法入参包含ObjectPool.Handle

      private Item(ObjectPool.Handle<Item> handle) {
        	this.handle = handle;
      }
      
    • 需要有一个对象池ObjectPool,使用Netty提供的ObjectPool.ObjectCreator,实现newObject方法,通过上面的构造方法创建需要池化的对象。

      private static final ObjectPool<Item> pool = ObjectPool.newPool(new ObjectPool.ObjectCreator<Item>() {
          @Override
          public Item newObject(ObjectPool.Handle<Item> handle) {
            return new Item(handle);
          }
      });
      
    • 为目标池化对象提供recycle方法,负责通过ObjectPool.Handle回收当前实例。

      public void recycle() {
        	this.handle.recycle(this);
      }
      
  • Netty如何抽象对象池?

    • ObjectPool:对象池,提供get方法获取对象。
    • ObjectCreator:对象创建器,提供newObject方法创建对象。
    • Handle:对象回收器,提供recycle方法回收对象。
  • Netty如何实现对象池?

    • RecyclerObjectPool实现ObjectPool提供获取对象方法;由用户实现ObjectCreator对象创建逻辑;Recycler.DefaultHandle实现对象回收。
    • Recycler是对象池的入口类,每个ObjectPool持有一个Recycler。
    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();
        }
    }
    
    • 线程池化,每个线程持有一个Stack对象池,减少资源竞争,由Recycler统一管理。
    // Recycler
    private final FastThreadLocal<Stack<T>> threadLocal = ...;
    

对象池.png

  • 如何处理同一个对象在不同线程创建和回收的场景?

    • 前提:对于一个对象池RecyclerObjectPool(或Recycler)的一个线程(ThreadLocal)的Stack
    • 当前线程创建和回收的对象,直接保存在Stack的elements数组中,每次获取对象也会优先从这里获取
    • 当前线程创建,其他线程回收的对象,会保存在Recycler的ThreadLocal类变量中,Map的key是创建这个对象的Stack容器,value存放当前线程帮助创建对象线程回收的对象。每次获取对象时,如果elements数组中取完了,会从其他线程帮助回收的对象里取
    // Recycler
    // 其他线程的stack实例 - 线程帮助其他线程的stack回收的对象放在WeakOrderQueue容器中
    private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
        new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
        @Override
        protected Map<Stack<?>, WeakOrderQueue> initialValue() {
            return new WeakHashMap<Stack<?>, WeakOrderQueue>();
        }
    };