2828import java .util .WeakHashMap ;
2929import java .util .concurrent .atomic .AtomicInteger ;
3030
31+ import static java .lang .Math .max ;
32+ import static java .lang .Math .min ;
33+
3134/**
3235 * Light-weight object pool based on a thread-local stack.
3336 *
@@ -48,8 +51,10 @@ public void recycle(Object object) {
4851 private static final int OWN_THREAD_ID = ID_GENERATOR .getAndIncrement ();
4952 // TODO: Some arbitrary large number - should adjust as we get more production experience.
5053 private static final int DEFAULT_INITIAL_MAX_CAPACITY = 262144 ;
54+
5155 private static final int DEFAULT_MAX_CAPACITY ;
5256 private static final int INITIAL_CAPACITY ;
57+ private static final int MAX_SHARED_CAPACITY_FACTOR ;
5358 private static final int LINK_CAPACITY ;
5459
5560 static {
@@ -60,30 +65,37 @@ public void recycle(Object object) {
6065 if (maxCapacity < 0 ) {
6166 maxCapacity = DEFAULT_INITIAL_MAX_CAPACITY ;
6267 }
63-
6468 DEFAULT_MAX_CAPACITY = maxCapacity ;
6569
70+ MAX_SHARED_CAPACITY_FACTOR = max (2 ,
71+ SystemPropertyUtil .getInt ("io.netty.recycler.maxSharedCapacityFactor" ,
72+ 2 ));
73+
6674 LINK_CAPACITY = MathUtil .findNextPositivePowerOfTwo (
67- Math . max (SystemPropertyUtil .getInt ("io.netty.recycler.linkCapacity" , 16 ), 16 ));
75+ max (SystemPropertyUtil .getInt ("io.netty.recycler.linkCapacity" , 16 ), 16 ));
6876
6977 if (logger .isDebugEnabled ()) {
7078 if (DEFAULT_MAX_CAPACITY == 0 ) {
7179 logger .debug ("-Dio.netty.recycler.maxCapacity: disabled" );
80+ logger .debug ("-Dio.netty.recycler.maxSharedCapacityFactor: disabled" );
7281 logger .debug ("-Dio.netty.recycler.linkCapacity: disabled" );
7382 } else {
7483 logger .debug ("-Dio.netty.recycler.maxCapacity: {}" , DEFAULT_MAX_CAPACITY );
84+ logger .debug ("-Dio.netty.recycler.maxSharedCapacityFactor: {}" , MAX_SHARED_CAPACITY_FACTOR );
7585 logger .debug ("-Dio.netty.recycler.linkCapacity: {}" , LINK_CAPACITY );
7686 }
7787 }
7888
79- INITIAL_CAPACITY = Math . min (DEFAULT_MAX_CAPACITY , 256 );
89+ INITIAL_CAPACITY = min (DEFAULT_MAX_CAPACITY , 256 );
8090 }
8191
8292 private final int maxCapacity ;
93+ private final int maxSharedCapacityFactor ;
94+
8395 private final FastThreadLocal <Stack <T >> threadLocal = new FastThreadLocal <Stack <T >>() {
8496 @ Override
8597 protected Stack <T > initialValue () {
86- return new Stack <T >(Recycler .this , Thread .currentThread (), maxCapacity );
98+ return new Stack <T >(Recycler .this , Thread .currentThread (), maxCapacity , maxSharedCapacityFactor );
8799 }
88100 };
89101
@@ -92,7 +104,17 @@ protected Recycler() {
92104 }
93105
94106 protected Recycler (int maxCapacity ) {
95- this .maxCapacity = Math .max (0 , maxCapacity );
107+ this (maxCapacity , MAX_SHARED_CAPACITY_FACTOR );
108+ }
109+
110+ protected Recycler (int maxCapacity , int maxSharedCapacityFactor ) {
111+ if (maxCapacity <= 0 ) {
112+ this .maxCapacity = 0 ;
113+ this .maxSharedCapacityFactor = 1 ;
114+ } else {
115+ this .maxCapacity = maxCapacity ;
116+ this .maxSharedCapacityFactor = max (1 , maxSharedCapacityFactor );
117+ }
96118 }
97119
98120 @ SuppressWarnings ("unchecked" )
@@ -201,6 +223,7 @@ private static final class Link extends AtomicInteger {
201223 private WeakOrderQueue next ;
202224 private final WeakReference <Thread > owner ;
203225 private final int id = ID_GENERATOR .getAndIncrement ();
226+ private final Stack <?> stack ;
204227
205228 WeakOrderQueue (Stack <?> stack , Thread thread ) {
206229 head = tail = new Link ();
@@ -209,6 +232,10 @@ private static final class Link extends AtomicInteger {
209232 next = stack .head ;
210233 stack .head = this ;
211234 }
235+ this .stack = stack ;
236+ // We allocated a Link so reserve the space
237+ boolean reserved = stack .reserveSpace (LINK_CAPACITY );
238+ assert reserved ;
212239 }
213240
214241 void add (DefaultHandle <?> handle ) {
@@ -217,7 +244,13 @@ void add(DefaultHandle<?> handle) {
217244 Link tail = this .tail ;
218245 int writeIndex ;
219246 if ((writeIndex = tail .get ()) == LINK_CAPACITY ) {
247+ if (!stack .reserveSpace (LINK_CAPACITY )) {
248+ // Drop it.
249+ return ;
250+ }
251+ // We allocate a Link so reserve the space
220252 this .tail = tail = tail .next = new Link ();
253+
221254 writeIndex = tail .get ();
222255 }
223256 tail .elements [writeIndex ] = handle ;
@@ -259,7 +292,7 @@ boolean transfer(Stack<?> dst) {
259292
260293 if (expectedCapacity > dst .elements .length ) {
261294 final int actualCapacity = dst .increaseCapacity (expectedCapacity );
262- srcEnd = Math . min (srcStart + actualCapacity - dstSize , srcEnd );
295+ srcEnd = min (srcStart + actualCapacity - dstSize , srcEnd );
263296 }
264297
265298 if (srcStart != srcEnd ) {
@@ -280,6 +313,9 @@ boolean transfer(Stack<?> dst) {
280313 dst .size = newDstSize ;
281314
282315 if (srcEnd == LINK_CAPACITY && head .next != null ) {
316+ // Add capacity back as the Link is GCed.
317+ stack .reclaimSpace (LINK_CAPACITY );
318+
283319 this .head = head .next ;
284320 }
285321
@@ -303,15 +339,35 @@ static final class Stack<T> {
303339 private DefaultHandle <?>[] elements ;
304340 private final int maxCapacity ;
305341 private int size ;
342+ private final AtomicInteger availableSharedCapacity ;
306343
307344 private volatile WeakOrderQueue head ;
308345 private WeakOrderQueue cursor , prev ;
309346
310- Stack (Recycler <T > parent , Thread thread , int maxCapacity ) {
347+ Stack (Recycler <T > parent , Thread thread , int maxCapacity , int maxSharedCapacityFactor ) {
311348 this .parent = parent ;
312349 this .thread = thread ;
313350 this .maxCapacity = maxCapacity ;
314- elements = new DefaultHandle [Math .min (INITIAL_CAPACITY , maxCapacity )];
351+ availableSharedCapacity = new AtomicInteger (max (maxCapacity / maxSharedCapacityFactor , LINK_CAPACITY ));
352+ elements = new DefaultHandle [min (INITIAL_CAPACITY , maxCapacity )];
353+ }
354+
355+ boolean reserveSpace (int space ) {
356+ assert space >= 0 ;
357+ for (;;) {
358+ int available = availableSharedCapacity .get ();
359+ if (available < space ) {
360+ return false ;
361+ }
362+ if (availableSharedCapacity .compareAndSet (available , available - space )) {
363+ return true ;
364+ }
365+ }
366+ }
367+
368+ void reclaimSpace (int space ) {
369+ assert space >= 0 ;
370+ availableSharedCapacity .addAndGet (space );
315371 }
316372
317373 int increaseCapacity (int expectedCapacity ) {
@@ -321,7 +377,7 @@ int increaseCapacity(int expectedCapacity) {
321377 newCapacity <<= 1 ;
322378 } while (newCapacity < expectedCapacity && newCapacity < maxCapacity );
323379
324- newCapacity = Math . min (newCapacity , maxCapacity );
380+ newCapacity = min (newCapacity , maxCapacity );
325381 if (newCapacity != elements .length ) {
326382 elements = Arrays .copyOf (elements , newCapacity );
327383 }
@@ -421,7 +477,7 @@ void push(DefaultHandle<?> item) {
421477 return ;
422478 }
423479 if (size == elements .length ) {
424- elements = Arrays .copyOf (elements , Math . min (size << 1 , maxCapacity ));
480+ elements = Arrays .copyOf (elements , min (size << 1 , maxCapacity ));
425481 }
426482
427483 elements [size ] = item ;
0 commit comments