1. 示例代码与运行结果

以下是一个使用 Netty Recycler 对象池的测试示例,展示了对象的获取、使用、回收及复用的过程。

public class RecyclerTest {
    static class WrapRecycler {

        private boolean tmp;
        private final static Recycler<WrapRecycler> RECYCLER = new Recycler<WrapRecycler>() {
            @Override
            protected WrapRecycler newObject(Handle<WrapRecycler> handle) {
                return new WrapRecycler(handle);
            }
        };

        Recycler.Handle<WrapRecycler> handle;

        WrapRecycler(Recycler.Handle<WrapRecycler> handle) {
            this.handle = handle;
            this.tmp = false;
        }

        static WrapRecycler getInstance() {
            return RECYCLER.get();
        }

        void recycle() {
            this.tmp = false;
            handle.recycle(this);
        }

        public boolean getTmp() {
            return tmp;
        }

        public void setTmp(boolean tmp) {
            this.tmp = tmp;
        }
    }

    public static void main(String[] args) {
        WrapRecycler instance = WrapRecycler.getInstance();
        System.out.println(instance.hashCode());
        System.out.println(instance.getTmp());
        instance.setTmp(true);
        System.out.println(instance.getTmp());
        instance.recycle();
        instance = WrapRecycler.getInstance();
        System.out.println(instance.hashCode());
        System.out.println(instance.getTmp());
    }
}

运行结果如下,两次获取的对象哈希值(hashCode)相同,且第二次获取时 tmp 状态被重置,说明是同一个对象被复用。

41359092
false
true
41359092
false

2. Recycler 类结构分析

2.1 整体架构

  • 抽象类设计Recycler 是一个抽象类。在目标类中创建对象池实例时,需要实现 newObject(Handle handle) 方法,用于创建特定类的实例。
  • 存储机制Recycler 对象池内部通过对象数组维护池中的对象实例,对外提供统一的对象创建和回收接口。
  • 核心方法

    • Recycler#get:获取对象池中对象的入口。如果有可用对象则直接返回,否则调用 newObject() 方法创建新对象。
    • Recycler#recycle:对象使用完毕后回收对象。注意:该方法只能回收当前线程创建的对象,且现已标记为 @Deprecated,不建议使用。建议使用 Recycler.Handle#recycle 方法,它可以回收非当前线程创建的对象,复用性和性能更好。
  • 核心组件Recycler 对象池的实现主要依赖三个核心组件:HandleWeakOrderQueueStack

2.2 Handle 组件

Handle 组件主要用来对外提供一个 recycle() 对象回收接口。每个池化对象在 newObject() 创建时都会绑定一个 Handle,用作该对象的回收凭证。

static final class DefaultHandle<T> implements Handle<T> {
    private int lastRecycledId;
    private int recycleId;

    boolean hasBeenRecycled;

    private Stack<?> stack;
    private 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.push(this);
    }
}
  • 对象绑定:每个 Handle 关联一个 value 字段,用于存放具体的池化对象。在对象池中,所有的池化对象都被这个 Handle 包装,Handle 是对象池管理的基本单位,Stack 中存储管理的也是 Handle 类。
  • 获取逻辑:调用 get() 方法从对象池中获取复用对象。如果对象池为空,则在 Stack 中创建新的 Handle 实例,并通过 newObject() 方法创建对象赋给 handle.value 属性。
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;
}

2.3 Stack 组件

Stack 组件通过 FastThreadLocal 类为每个线程维护一个与线程绑定的栈,用来存储复用的对象。

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);
    }
};
  • 线程绑定:线程在通过 Recyclerget() 方法获取复用对象时,首先获取与当前线程绑定的 Stack,再从 StackpopHandle 包装实例。
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;
}

2.4 WeakOrderQueue 组件

WeakOrderQueueRecycler 的一个内部私有类,用来暂存待回收的对象。Handle.recycle() 可以回收不是当前线程创建的对象,主要依靠 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>();
    }
};
  • 回收逻辑:如果是当前线程创建的对象,直接就把对象放到当前线程对应的 Stack 中;如果不是,则放入 WeakOrderQueue 中。每个线程维护着一个 WeakHashMap
public void recycle(Object object) {
    if (object != value) {
        throw new IllegalArgumentException("object does not belong to handle");
    }
    stack.push(this);
}
void push(DefaultHandle<?> item) {
    Thread currentThread = Thread.currentThread();
    if (thread == currentThread) {
        pushNow(item);
    } else {
        pushLater(item, currentThread);
    }
}
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);
}
  • 对象 scavenging:在从 Stack 获取对象池时,如果 Stack 中可用对象为空,会尝试遍历多线程对应的 WeakOrderQueue 链表中回收恢复对象。
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;
}
DefaultHandle<T> pop() {
    int size = this.size;
    if (size == 0) {
        if (!scavenge()) {
            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;
}
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.next;
        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;
}

20171124194628835.png

3. 总结

  • Netty 实现的轻量级对象池实现了对象的复用和回收,核心组件包括 StackWeakOrderQueueHandle
  • Stack 相当于 MyBatis 的一级缓存,与线程绑定。线程内对象的获取和回收都使用绑定的 Stack;复用对象时直接从 Stack 中申请,使用完后如果对象是自己创建的则放入 Stack
  • 在对象回收时,如果对象不是当前线程创建的,则不能放入 Stack 中回收,而是放入 WeakOrderQueue 中。所有的 Queue 组成一个链表作为对象回收的仓库,当 Stack 中无可用的对象时会遍历链表回收对象,实现了多线程之间对象回收的共享。

20171124194642988.png

说明

本文基于 Netty 早期版本(约 4.1.x 时期)的 Recycler 实现进行分析。Netty 后续版本中对象池内部实现(如 StackPool 结构)可能有所演进,具体细节请以实际使用的 Netty 版本源码为准。