2017年写这篇文章的时候,关于Netty对象池Recylcer.java源码解读的文章还很少,但当时由于时间匆忙,仅仅给出了Recycler使用的示例代码,但是没有做深入的源码解读。
今天(2019-02-18)回头过来整理的时候,发现网上一下子涌现出了许多篇介绍Recycler.java的文章,并且,已经有介绍的比较详细的文章,所以本人已经不打算进一步分析源码了。此处暂且列出几篇优秀的博文供大家查阅。
1. Netty之Recycler
2. netty源码分析4 - Recycler对象池的设计
但是网站的这些文章,虽然已经详细分析了Recycler.java。但是却没有一张个人认为拿得出手的Recycler图示。所以,本人仅仅提供一张更加详细的Recycler内部结构图,希望这张图能够帮助有兴趣的朋友们深入的理解这个类。
Recycler结构简图:
Recycler用法示例
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class RecyclerTest01 {
private static AtomicInteger objId = new AtomicInteger(1);
private static CountDownLatch latch = new CountDownLatch(1);
private static PooledObj[] objSlot = new PooledObj[4];
private static CountDownLatch async_thread_latch = new CountDownLatch(2);
private static AtomicInteger async_thread_id = new AtomicInteger(99);
private static Recycler<PooledObj> pool = new Recycler<PooledObj>() {
@Override
protected PooledObj newObject(Handle<PooledObj> handle) {
PooledObj obj = new PooledObj("obj-" + objId.getAndIncrement(), handle);
return obj;
}
};
static class PooledObj {
private Recycler.Handle<PooledObj> handle = null;
private String name = null;
public PooledObj(String name, Recycler.Handle<PooledObj> handle) {
this.name = name;
this.handle = handle;
}
public Recycler.Handle<PooledObj> getHandle() {
return handle;
}
public void setHandle(Recycler.Handle<PooledObj> handle) {
this.handle = handle;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public void recycle() {
handle.recycle(this);
}
}
static class GetRunnable implements Runnable {
private CountDownLatch latch;
private boolean nosync;
private int index;
public GetRunnable(CountDownLatch latch, boolean nosync, int index) {
this.latch = latch;
this.nosync = nosync;
this.index = index;
}
@Override
public void run() {
PooledObj obj = pool.get();
objSlot[index] = obj;
System.out.println(Thread.currentThread().getName() + " 获取对象: " + obj);
if (!nosync) {
try {
System.out.println(Thread.currentThread().getName() + " 僵住等待停止");
latch.await();
PooledObj anotherObj = pool.get();
System.out.println(Thread.currentThread().getName() + " 线程被唤醒, 并且得到对象" + anotherObj);
String name = Thread.currentThread().getName();
Thread asyncRecycleThread = new Thread(() -> {
anotherObj.recycle();
async_thread_latch.countDown();
}, "async-thread-" + async_thread_id.getAndIncrement() + "-versus-" + name );
asyncRecycleThread.setDaemon(false);
asyncRecycleThread.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testRecycler() {
PooledObj obj0 = pool.get();
objSlot[0] = obj0;
System.out.println("主线程从对象池获取对象" + obj0);
Thread g1 = new Thread(new GetRunnable(latch, false, 1), "thread-1");
g1.start();
Thread g2 = new Thread(new GetRunnable(latch, true, 2), "thread-2");
g2.start();
Thread g3 = new Thread(new GetRunnable(latch, false, 3), "thread-3");
g3.start();
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
objSlot[0].recycle();
objSlot[1].recycle();
objSlot[2].recycle();
objSlot[3].recycle();
PooledObj obj = pool.get();
System.out.println("回收后取到对象: " + obj);
PooledObj obj2 = pool.get();
System.out.println("回收后取到对象: " + obj2);
PooledObj obj3 = pool.get();
System.out.println("回收后取到对象: " + obj3);
PooledObj obj4 = pool.get();
System.out.println("回收后取到对象: " + obj4);
obj.recycle();
obj2.recycle();
obj3.recycle();
obj4.recycle();
latch.countDown();
try {
async_thread_latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行上述测试代码,控制台打印如下日志,可见示例代码已经模拟了对象被自身线程回收和被其他线程回收两种情况:
main get STACK: main-stack
主线程从对象池获取对象obj-1
thread-1 get STACK: thread-1-stack
thread-1 获取对象: obj-2
thread-1 僵住等待停止
thread-2 get STACK: thread-2-stack
thread-2 获取对象: obj-3
thread-3 get STACK: thread-3-stack
thread-3 获取对象: obj-4
thread-3 僵住等待停止
回收后取到对象: obj-1
main get STACK: main-stack
回收后取到对象: obj-5
main get STACK: main-stack
回收后取到对象: obj-6
main get STACK: main-stack
回收后取到对象: obj-7
thread-1 线程被唤醒, 并且得到对象obj-2
thread-3 线程被唤醒, 并且得到对象obj-4
异步回收线程async-thread-99-versus-thread-1 开始回收对象
异步回收线程async-thread-100-versus-thread-3 开始回收对象
Recycler源码如下,下述的源码为了配合上面的用法示例,本人已经在必要的地方添加了日志。注: Recycler对象池相关的源码已经抽取为一个框架
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import static io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo;
import static java.lang.Math.max;
import static java.lang.Math.min;
public abstract class Recycler<T> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance( Recycler.class);
@SuppressWarnings("rawtypes")
private static final Handle NOOP_HANDLE = new Handle() {
@Override
public void recycle(Object object) {
}
};
private static final AtomicInteger ID_GENERATOR = new AtomicInteger(Integer.MIN_VALUE);
private static final int OWN_THREAD_ID = ID_GENERATOR.getAndIncrement();
private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 4 * 1024;
private static final int DEFAULT_MAX_CAPACITY_PER_THREAD;
private static final int INITIAL_CAPACITY;
private static final int MAX_SHARED_CAPACITY_FACTOR;
private static final int MAX_DELAYED_QUEUES_PER_THREAD;
private static final int LINK_CAPACITY;
private static final int RATIO;
static {
int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty. maxCapacityPerThread",
SystemPropertyUtil.getInt("io.netty. maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD));
if (maxCapacityPerThread < 0) {
maxCapacityPerThread = DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD;
}
DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread;
MAX_SHARED_CAPACITY_FACTOR = max(2,
SystemPropertyUtil.getInt("io.netty. maxSharedCapacityFactor",
2));
MAX_DELAYED_QUEUES_PER_THREAD = max(0,
SystemPropertyUtil.getInt("io.netty. maxDelayedQueuesPerThread",
NettyRuntime.availableProcessors() * 2));
LINK_CAPACITY = safeFindNextPositivePowerOfTwo(
max(SystemPropertyUtil.getInt("io.netty. linkCapacity", 16), 16));
RATIO = safeFindNextPositivePowerOfTwo(SystemPropertyUtil.getInt("io.netty. ratio", 8));
if (logger.isDebugEnabled()) {
if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
logger.debug("-Dio.netty. maxCapacityPerThread: disabled");
logger.debug("-Dio.netty. maxSharedCapacityFactor: disabled");
logger.debug("-Dio.netty. linkCapacity: disabled");
logger.debug("-Dio.netty. ratio: disabled");
} else {
logger.debug("-Dio.netty. maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD);
logger.debug("-Dio.netty. maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR);
logger.debug("-Dio.netty. linkCapacity: {}", LINK_CAPACITY);
logger.debug("-Dio.netty. ratio: {}", RATIO);
}
}
INITIAL_CAPACITY = min(DEFAULT_MAX_CAPACITY_PER_THREAD, 256);
}
private final int maxCapacityPerThread;
private final int maxSharedCapacityFactor;
private final int ratioMask;
private final int maxDelayedQueuesPerThread;
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) {
if (value.threadRef.get() == Thread.currentThread()) {
if (DELAYED_RECYCLED.isSet()) {
DELAYED_RECYCLED.get().remove(value);
}
}
}
};
protected Recycler() {
this(DEFAULT_MAX_CAPACITY_PER_THREAD);
}
protected Recycler(int maxCapacityPerThread) {
this(maxCapacityPerThread, MAX_SHARED_CAPACITY_FACTOR);
}
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor) {
this(maxCapacityPerThread, maxSharedCapacityFactor, RATIO, MAX_DELAYED_QUEUES_PER_THREAD);
}
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
int ratio, int maxDelayedQueuesPerThread) {
ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1;
if (maxCapacityPerThread <= 0) {
this.maxCapacityPerThread = 0;
this.maxSharedCapacityFactor = 1;
this.maxDelayedQueuesPerThread = 0;
} else {
this.maxCapacityPerThread = maxCapacityPerThread;
this.maxSharedCapacityFactor = max(1, maxSharedCapacityFactor);
this.maxDelayedQueuesPerThread = max(0, maxDelayedQueuesPerThread);
}
}
@SuppressWarnings("unchecked")
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;
}
@Deprecated
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;
}
final int threadLocalCapacity() {
return threadLocal.get().elements.length;
}
final int threadLocalSize() {
return threadLocal.get().size;
}
protected abstract T newObject( Handle<T> handle);
public interface Handle<T> {
void recycle(T object);
}
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) {
System.out.println(Thread.currentThread().getName() + " get 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);
}
@Override
public String toString() {
return "handle-" + value;
}
}
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>();
}
};
private static final class WeakOrderQueue {
static final WeakOrderQueue DUMMY = new WeakOrderQueue();
@SuppressWarnings("serial")
static final class Link extends AtomicInteger {
private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];
private int readIndex;
Link next;
}
static final class Head {
private final AtomicInteger availableSharedCapacity;
Link link;
Head(AtomicInteger availableSharedCapacity) {
this.availableSharedCapacity = availableSharedCapacity;
}
@Override
protected void finalize() throws Throwable {
try {
super.finalize();
} finally {
Link head = link;
link = null;
while (head != null) {
reclaimSpace(LINK_CAPACITY);
Link next = head.next;
head.next = null;
head = next;
}
}
}
void reclaimSpace(int space) {
assert space >= 0;
availableSharedCapacity.addAndGet(space);
}
boolean reserveSpace(int space) {
return reserveSpace(availableSharedCapacity, space);
}
static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) {
assert space >= 0;
for (;;) {
int available = availableSharedCapacity.get();
if (available < space) {
return false;
}
if (availableSharedCapacity.compareAndSet(available, available - space)) {
return true;
}
}
}
}
private final Head head;
private Link tail;
private WeakOrderQueue next;
private final WeakReference<Thread> owner;
private final int id = ID_GENERATOR.getAndIncrement();
private WeakOrderQueue() {
owner = null;
head = new Head(null);
}
private WeakOrderQueue(Stack<?> stack, Thread thread) {
tail = new Link();
head = new Head(stack.availableSharedCapacity);
head.link = tail;
owner = new WeakReference<Thread>(thread);
}
static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
final WeakOrderQueue queue = new WeakOrderQueue(stack, thread);
stack.setHead(queue);
if (thread.getName().startsWith("async-thread-")) {
System.out.println("异步回收线程"+ thread.getName() + " 开始回收对象");
}
return queue;
}
private void setNext(WeakOrderQueue next) {
assert next != this;
this.next = next;
}
static WeakOrderQueue allocate(Stack<?> stack, Thread thread) {
return Head.reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)
? newQueue(stack, thread) : null;
}
void add(DefaultHandle<?> handle) {
handle.lastRecycledId = id;
Link tail = this.tail;
int writeIndex;
if ((writeIndex = tail.get()) == LINK_CAPACITY) {
if (!head.reserveSpace(LINK_CAPACITY)) {
return;
}
this.tail = tail = tail.next = new Link();
writeIndex = tail.get();
}
tail.elements[writeIndex] = handle;
handle.stack = null;
tail.lazySet(writeIndex + 1);
}
boolean hasFinalData() {
return tail.readIndex != tail.get();
}
@SuppressWarnings("rawtypes")
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;
}
this.head.link = head = head.next;
}
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)) {
continue;
}
element.stack = dst;
dstElems[newDstSize ++] = element;
}
if (srcEnd == LINK_CAPACITY && head.next != null) {
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 {
return false;
}
}
}
static final class Stack<T> {
final Recycler<T> parent;
final WeakReference<Thread> threadRef;
final AtomicInteger availableSharedCapacity;
final int maxDelayedQueues;
private final int maxCapacity;
private final int ratioMask;
private DefaultHandle<?>[] elements;
private int size;
private int handleRecycleCount = -1;
private WeakOrderQueue cursor, prev;
private volatile WeakOrderQueue head;
private String stackThreadName;
Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
int ratioMask, int maxDelayedQueues) {
this.parent = parent;
threadRef = new WeakReference<Thread>(thread);
this.maxCapacity = maxCapacity;
availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
this.ratioMask = ratioMask;
this.maxDelayedQueues = maxDelayedQueues;
this.stackThreadName = thread.getName();
}
synchronized void setHead(WeakOrderQueue queue) {
queue.setNext(head);
head = queue;
}
int increaseCapacity(int expectedCapacity) {
int newCapacity = elements.length;
int maxCapacity = this.maxCapacity;
do {
newCapacity <<= 1;
} while (newCapacity < expectedCapacity && newCapacity < maxCapacity);
newCapacity = min(newCapacity, maxCapacity);
if (newCapacity != elements.length) {
elements = Arrays.copyOf(elements, newCapacity);
}
return newCapacity;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
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 scavenge() {
if (scavengeSome()) {
return true;
}
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;
}
void push(DefaultHandle<?> item) {
Thread currentThread = Thread.currentThread();
if (threadRef.get() == currentThread) {
pushNow(item);
} else {
pushLater(item, currentThread);
}
}
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;
}
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);
}
boolean dropHandle(DefaultHandle<?> handle) {
if (!handle.hasBeenRecycled) {
if ((++handleRecycleCount & ratioMask) != 0) {
return true;
}
handle.hasBeenRecycled = true;
}
return false;
}
DefaultHandle<T> newHandle() {
return new DefaultHandle<T>(this);
}
@Override
public String toString() {
return stackThreadName + "-stack";
}
}
}