Recycler
Recycler是Netty经常使用的对象池抽象类,通过重用对象,能够避免频繁创建对象和销毁对象带来的损耗。
1. Recycler 抽象类介绍
1. 使用了FastThreadLocal
// 每个线程都有自己的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,
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);
}
}
}
};
// 每个线程stack对应的WeakOrderQueue, 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>();
}
};
2. 核心方法
protected abstract T newObject(Handle<T> handle);
public final T get();
2. 怎么使用
static class User {
private String name;
private Recycler.Handle<User> handle;
public User(Recycler.Handle<User> handle) {
this.handle = handle;
}
public void recycle() {
handle.recycle(this);
}
}
private static final Recycler<User> userRecycler = new Recycler<User>() {
@Override
protected User newObject(Handle<User> handle) {
return new User(handle);
}
};
@Test
public void test() {
// 1、从对象池获取对象
User user1 = userRecycler.get();
// 2、设置对象并使用
user1.setName("hello recycle");
// 3、对象恢复出厂设置
user1.setName(null);
// 4、回收对象到对象池
user1.recycle();
// 5、从回收池获取对象
User user2 = userRecycler.get();
System.out.println(user1 == user2);
}
测试结果 true
3. get()方法
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;
}
1.若maxCapacityPerThread =0时,就返回一个空实现recycle方法的Handle实体类
2.获取当前线程的Stack Stack<T> stack = threadLocal.get(); 这个利用FastThreadLocal
3.stack.pop()
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;
}
Stack实体类主要实现方式是数组形式,当size=0时,说明当前线程中stack没有回收到对象,直接调用scavenge()方法,这个后面会去分析,当size>0时,直接从数组中取出对象。
4.当pop返回null,直接调用newHandle()
4. Handle.recycel()
public void recycle(Object object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
stack.push(this);
}
一般回收的逻辑是由用户去写,Handel不会去处理,Handle.recycel()主要是执行stack.push(this);
stack.push()
void push(DefaultHandle<?> item) {
Thread currentThread = Thread.currentThread();
if (threadRef.get() == currentThread) {
// The current Thread is the thread that belongs to the Stack, we can try to push the object now.
pushNow(item);
} else {
// The current Thread is not the one that belongs to the Stack
// (or the Thread that belonged to the Stack was collected already), we need to signal that the push
// happens later.
pushLater(item, currentThread);
}
}
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)) {
// Hit the maximum capacity or should drop - drop the possibly youngest object.
return;
}
if (size == elements.length) {
elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
}
elements[size] = item;
this.size = size + 1;
}
1.1 判断当前size是否大于最大容量
1.2 dropHandle()
boolean dropHandle(DefaultHandle<?> handle) {
// 1.判断有没有被回收
if (!handle.hasBeenRecycled) {
// 2. handleRecycleCount 初始值是-1,正常情况是0
// 若不是0,表示已经被回收了,直接放弃
if ((++handleRecycleCount & ratioMask) != 0) {
// Drop the object.
return true;
}
handle.hasBeenRecycled = true;
}
return false;
}
1.3 如果数组满了需要扩容一倍,最大 4096(默认)
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);
}
2.1 获取当前线程delayedRecycled
2.2 根据stack,去获取WeakOrderQueue,注意这个stack不是当前线程的stack,这样每个线程可以和其他线程通过stack关联WeakOrderQueue,这样才能达到其他线程可以回收当前线程的对象,会存储在WeakOrderQueue中link,而Link类中包含DefaultHandle<?>[] 默认是16个
2.3 获取的queue为null时,先判断delayedRecycled.size() >= maxDelayedQueues,然后尝试创建一个queue,链接到这个 Stack 的 head 节点前
2.4 最终将回收对象放到queue中
void add(DefaultHandle<?> handle) {
handle.lastRecycledId = id;
Link tail = this.tail;
int writeIndex;
if ((writeIndex = tail.get()) == LINK_CAPACITY) {
if (!reserveSpace(availableSharedCapacity, LINK_CAPACITY)) {
// Drop it.
return;
}
// We allocate a Link so reserve the space
this.tail = tail = tail.next = new Link();
writeIndex = tail.get();
}
tail.elements[writeIndex] = handle;
handle.stack = null;
tail.lazySet(writeIndex + 1);
}
1.如果这个 tiail 节点满了,查看是否还有共享空间,如果没了,就丢弃这个实例
2.反之新建一个 Link,追加到 tail 节点的尾部。然后,将数据插入新 tail 的数组
5. stack.scavenge()
这个主要从其他线程中,看有没有回收的线程
boolean scavenge() {
// continue an existing scavenge, if any
if (scavengeSome()) {
return true;
}
// reset our scavenge cursor
prev = null;
cursor = head;
return false;
}
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;
}
1.拿到这个 stack 的 queue,调用这个 queue 的 transfer 方法,如果成功,结束循环。
2.如果 queue 所在的线程被回收了,就将这个线程对应的 queue 中的所有数据全部转移到 stack 中。
总结:
Netty实现了一个相对轻量的对象池。通过使用 threadLocal,避免了多线程下取数据时可能出现的线程安全问题.
下面就是Netty设计recycle图