/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/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;importstatic io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo;importstatic java.lang.Math.max;importstatic java.lang.Math.min;/**
* Light-weight object pool based on a thread-local stack.
*
* @param <T> the type of the pooled object
*/publicabstractclassRecycler<T>{privatestaticfinal InternalLogger logger = InternalLoggerFactory.getInstance( Recycler.class);@SuppressWarnings("rawtypes")privatestaticfinal Handle NOOP_HANDLE =newHandle(){@Overridepublicvoidrecycle(Object object){// NOOP}};privatestaticfinal AtomicInteger ID_GENERATOR =newAtomicInteger(Integer.MIN_VALUE);privatestaticfinalint OWN_THREAD_ID = ID_GENERATOR.getAndIncrement();privatestaticfinalint DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD =4*1024;// Use 4k instances as default.privatestaticfinalint DEFAULT_MAX_CAPACITY_PER_THREAD;privatestaticfinalint INITIAL_CAPACITY;privatestaticfinalint MAX_SHARED_CAPACITY_FACTOR;privatestaticfinalint MAX_DELAYED_QUEUES_PER_THREAD;privatestaticfinalint LINK_CAPACITY;privatestaticfinalint RATIO;static{// In the future, we might have different maxCapacity for different object types.// e.g. io.netty. maxCapacity.writeTask// io.netty. maxCapacity.outboundBufferint 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",// We use the same value as default EventLoop number
NettyRuntime.availableProcessors()*2));
LINK_CAPACITY =safeFindNextPositivePowerOfTwo(max(SystemPropertyUtil.getInt("io.netty. linkCapacity",16),16));// By default we allow one push to a Recycler for each 8th try on handles that were never recycled before.// This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation// bursts.
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);}privatefinalint maxCapacityPerThread;privatefinalint maxSharedCapacityFactor;privatefinalint ratioMask;privatefinalint maxDelayedQueuesPerThread;privatefinal FastThreadLocal< Stack<T>> threadLocal =newFastThreadLocal< Stack<T>>(){@Overrideprotected Stack<T>initialValue(){returnnewStack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
ratioMask, maxDelayedQueuesPerThread);}@OverrideprotectedvoidonRemoval( Stack<T> value){// Let us remove the WeakOrderQueue from the WeakHashMap directly if its safe to remove some overheadif(value.threadRef.get()== Thread.currentThread()){if(DELAYED_RECYCLED.isSet()){
DELAYED_RECYCLED.get().remove(value);}}}};protectedRecycler(){this(DEFAULT_MAX_CAPACITY_PER_THREAD);}protectedRecycler(int maxCapacityPerThread){this(maxCapacityPerThread, MAX_SHARED_CAPACITY_FACTOR);}protectedRecycler(int maxCapacityPerThread,int maxSharedCapacityFactor){this(maxCapacityPerThread, maxSharedCapacityFactor, RATIO, MAX_DELAYED_QUEUES_PER_THREAD);}protectedRecycler(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")publicfinal T get(){if(maxCapacityPerThread ==0){returnnewObject(( 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 use {@link Handle#recycle(Object)}.
*/@Deprecatedpublicfinalbooleanrecycle(T o, Handle<T> handle){if(handle == NOOP_HANDLE){returnfalse;}
DefaultHandle<T> h =( DefaultHandle<T>) handle;if(h.stack.parent !=this){returnfalse;}
h.recycle(o);returntrue;}finalintthreadLocalCapacity(){return threadLocal.get().elements.length;}finalintthreadLocalSize(){return threadLocal.get().size;}protectedabstract T newObject( Handle<T> handle);publicinterfaceHandle<T>{voidrecycle(T object);}staticfinalclassDefaultHandle<T>implementsHandle<T>{privateint lastRecycledId;privateint recycleId;boolean hasBeenRecycled;private Stack<?> stack;private Object value;DefaultHandle( Stack<?> stack){
System.out.println(Thread.currentThread().getName()+" get STACK: "+ stack);this.stack = stack;}@Overridepublicvoidrecycle(Object object){if(object != value){thrownewIllegalArgumentException("object does not belong to handle");}
Stack<?> stack =this.stack;if(lastRecycledId != recycleId || stack == null){thrownewIllegalStateException("recycled already");}
stack.push(this);}@Overridepublic String toString(){return"handle-"+ value;}}privatestaticfinal FastThreadLocal<Map< Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =newFastThreadLocal<Map< Stack<?>, WeakOrderQueue>>(){@Overrideprotected Map< Stack<?>, WeakOrderQueue>initialValue(){returnnewWeakHashMap< Stack<?>, WeakOrderQueue>();}};// a queue that makes only moderate guarantees about visibility: items are seen in the correct order,// but we aren't absolutely guaranteed to ever see anything at all, thereby keeping the queue cheap to maintainprivatestaticfinalclassWeakOrderQueue{staticfinal WeakOrderQueue DUMMY =newWeakOrderQueue();// Let Link extend AtomicInteger for intrinsics. The Link itself will be used as writerIndex.@SuppressWarnings("serial")staticfinalclassLinkextendsAtomicInteger{privatefinal DefaultHandle<?>[] elements =newDefaultHandle[LINK_CAPACITY];privateint readIndex;
Link next;}// This act as a place holder for the head Link but also will reclaim space once finalized.// Its important this does not hold any reference to either Stack or WeakOrderQueue.staticfinalclassHead{privatefinal AtomicInteger availableSharedCapacity;
Link link;Head(AtomicInteger availableSharedCapacity){this.availableSharedCapacity = availableSharedCapacity;}/// TODO: In the future when we move to Java9+ we should use java.lang.ref.Cleaner.@Overrideprotectedvoidfinalize()throws Throwable {try{super.finalize();}finally{
Link head = link;
link = null;while(head != null){reclaimSpace(LINK_CAPACITY);
Link next = head.next;// Unlink to help GC and guard against GC nepotism.
head.next = null;
head = next;}}}voidreclaimSpace(int space){assert space >=0;
availableSharedCapacity.addAndGet(space);}booleanreserveSpace(int space){returnreserveSpace(availableSharedCapacity, space);}staticbooleanreserveSpace(AtomicInteger availableSharedCapacity,int space){assert space >=0;for(;;){int available = availableSharedCapacity.get();if(available < space){returnfalse;}if(availableSharedCapacity.compareAndSet(available, available - space)){returntrue;}}}}// chain of data itemsprivatefinal Head head;private Link tail;// pointer to another queue of delayed items for the same stackprivate WeakOrderQueue next;privatefinal WeakReference<Thread> owner;privatefinalint id = ID_GENERATOR.getAndIncrement();privateWeakOrderQueue(){
owner = null;
head =newHead(null);}privateWeakOrderQueue(Stack<?> stack, Thread thread){
tail =newLink();// Its important that we not store the Stack itself in the WeakOrderQueue as the Stack also is used in// the WeakHashMap as key. So just store the enclosed AtomicInteger which should allow to have the// Stack itself GCed.
head =newHead(stack.availableSharedCapacity);
head.link = tail;
owner =newWeakReference<Thread>(thread);}static WeakOrderQueue newQueue(Stack<?> stack, Thread thread){final WeakOrderQueue queue =newWeakOrderQueue(stack, thread);// Done outside of the constructor to ensure WeakOrderQueue.this does not escape the constructor and so// may be accessed while its still constructed.
stack.setHead(queue);if(thread.getName().startsWith("async-thread-")){
System.out.println("异步回收线程"+ thread.getName()+" 开始回收对象");}return queue;}privatevoidsetNext(WeakOrderQueue next){assert next !=this;this.next = next;}/**
* Allocate a new {@link WeakOrderQueue} or return {@code null} if not possible.
*/static WeakOrderQueue allocate(Stack<?> stack, Thread thread){// We allocated a Link so reserve the spacereturn Head.reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)?newQueue(stack, thread): null;}voidadd(DefaultHandle<?> handle){
handle.lastRecycledId = id;
Link tail =this.tail;int writeIndex;if((writeIndex = tail.get())== LINK_CAPACITY){if(!head.reserveSpace(LINK_CAPACITY)){// Drop it.return;}// We allocate a Link so reserve the spacethis.tail = tail = tail.next =newLink();
writeIndex = tail.get();}
tail.elements[writeIndex]= handle;
handle.stack = null;// we lazy set to ensure that setting stack to null appears before we unnull it in the owning thread;// this also means we guarantee visibility of an element in the queue if we see the index updated
tail.lazySet(writeIndex +1);}booleanhasFinalData(){return tail.readIndex != tail.get();}// transfer as many items as we can from this queue to the stack, returning true if any were transferred@SuppressWarnings("rawtypes")booleantransfer(Stack<?> dst){
Link head =this.head.link;if(head == null){returnfalse;}if(head.readIndex == LINK_CAPACITY){if(head.next == null){returnfalse;}this.head.link = head = head.next;}finalint srcStart = head.readIndex;int srcEnd = head.get();finalint srcSize = srcEnd - srcStart;if(srcSize ==0){returnfalse;}finalint dstSize = dst.size;finalint expectedCapacity = dstSize + srcSize;if(expectedCapacity > dst.elements.length){finalint 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;}elseif(element.recycleId != element.lastRecycledId){thrownewIllegalStateException("recycled already");}
srcElems[i]= null;if(dst.dropHandle(element)){// Drop the object.continue;}
element.stack = dst;
dstElems[newDstSize ++]= element;}if(srcEnd == LINK_CAPACITY && head.next != null){// 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){returnfalse;}
dst.size = newDstSize;returntrue;}else{// The destination stack is full already.returnfalse;}}}staticfinalclassStack<T>{// we keep a queue of per-thread queues, which is appended to once only, each time a new thread other// than the stack owner recycles: when we run out of items in our stack we iterate this collection// to scavenge those that can be reused. this permits us to incur minimal thread synchronisation whilst// still recycling all items.final Recycler<T> parent;// We store the Thread in a WeakReference as otherwise we may be the only ones that still hold a strong// Reference to the Thread itself after it died because DefaultHandle will hold a reference to the Stack.//// The biggest issue is if we do not use a WeakReference the Thread may not be able to be collected at all if// the user will store a reference to the DefaultHandle somewhere and never clear this reference (or not clear// it in a timely manner).final WeakReference<Thread> threadRef;final AtomicInteger availableSharedCapacity;finalint maxDelayedQueues;privatefinalint maxCapacity;privatefinalint ratioMask;private DefaultHandle<?>[] elements;privateint size;privateint handleRecycleCount =-1;// Start with -1 so the first one will be recycled.private WeakOrderQueue cursor, prev;privatevolatile WeakOrderQueue head;private String stackThreadName;Stack(Recycler<T> parent, Thread thread,int maxCapacity,int maxSharedCapacityFactor,int ratioMask,int maxDelayedQueues){this.parent = parent;
threadRef =newWeakReference<Thread>(thread);this.maxCapacity = maxCapacity;
availableSharedCapacity =newAtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
elements =newDefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];this.ratioMask = ratioMask;this.maxDelayedQueues = maxDelayedQueues;this.stackThreadName = thread.getName();}// Marked as synchronized to ensure this is serialized.synchronizedvoidsetHead(WeakOrderQueue queue){
queue.setNext(head);
head = queue;}intincreaseCapacity(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){thrownewIllegalStateException("recycled multiple times");}
ret.recycleId =0;
ret.lastRecycledId =0;this.size = size;return ret;}booleanscavenge(){// continue an existing scavenge, if anyif(scavengeSome()){returntrue;}// reset our scavenge cursor
prev = null;
cursor = head;returnfalse;}booleanscavengeSome(){
WeakOrderQueue prev;
WeakOrderQueue cursor =this.cursor;if(cursor == null){
prev = null;
cursor = head;if(cursor == null){returnfalse;}}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 the thread associated with the queue is gone, unlink it, after// performing a volatile read to confirm there is no data left to collect.// We never unlink the first queue, as we don't want to synchronize on updating the head.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;}voidpush(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);}}privatevoidpushNow(DefaultHandle<?> item){if((item.recycleId | item.lastRecycledId)!=0){thrownewIllegalStateException("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;}privatevoidpushLater(DefaultHandle<?> item, Thread thread){// we don't want to have a ref to the queue as the value in our weak map// so we null it out; to ensure there are no races with restoring it later// we impose a memory ordering here (no-op on x86)
Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
WeakOrderQueue queue = delayedRecycled.get(this);if(queue == null){if(delayedRecycled.size()>= maxDelayedQueues){// Add a dummy queue so we know we should drop the object
delayedRecycled.put(this, WeakOrderQueue.DUMMY);return;}// Check if we already reached the maximum number of delayed queues and if we can allocate at all.if((queue = WeakOrderQueue.allocate(this, thread))== null){// drop objectreturn;}
delayedRecycled.put(this, queue);}elseif(queue == WeakOrderQueue.DUMMY){// drop objectreturn;}
queue.add(item);}booleandropHandle(DefaultHandle<?> handle){if(!handle.hasBeenRecycled){if((++handleRecycleCount & ratioMask)!=0){// Drop the object.returntrue;}
handle.hasBeenRecycled =true;}returnfalse;}
DefaultHandle<T>newHandle(){returnnewDefaultHandle<T>(this);}@Overridepublic String toString(){return stackThreadName +"-stack";}}}