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
中检索,如果查到了,就整体复制到Stack
的elements
中。Link继承AtomicInteger,所以Link还拥有AtomicInteger的一切特性。
二、组件关系
单从上面各个组件的介绍,无法实际理解他们之间的关系;下面就根据下图整体介绍一下:
(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对象池源码,如有转载,请声明!