Skip to content

Package: ByteBufferManager$SmallByteBufferWrapper

ByteBufferManager$SmallByteBufferWrapper

nameinstructionbranchcomplexitylinemethod
ByteBufferManager.SmallByteBufferWrapper(ByteBufferManager, ByteBuffer)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
dispose()
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
recycle()
M: 27 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
wrapByteBuffer(ByteBuffer)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2008, 2020 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.glassfish.grizzly.memory;
18:
19: import java.io.UnsupportedEncodingException;
20: import java.nio.ByteBuffer;
21: import java.nio.charset.Charset;
22: import java.util.Arrays;
23:
24: import org.glassfish.grizzly.Cacheable;
25: import org.glassfish.grizzly.ThreadCache;
26: import org.glassfish.grizzly.monitoring.MonitoringConfig;
27: import org.glassfish.grizzly.monitoring.MonitoringUtils;
28:
29: /**
30: * The simple Buffer manager implementation, which works as wrapper above {@link ByteBuffer}s. It's possible to work
31: * either with direct or heap {@link ByteBuffer}s.
32: *
33: * @see MemoryManager
34: * @see ByteBuffer
35: *
36: * @author Jean-Francois Arcand
37: * @author Alexey Stashok
38: */
39: public class ByteBufferManager extends AbstractMemoryManager<ByteBufferWrapper> implements WrapperAware, ByteBufferAware {
40:
41: /**
42: * TODO: Document
43: */
44: public static final int DEFAULT_SMALL_BUFFER_SIZE = 32;
45:
46: private static final ThreadCache.CachedTypeIndex<TrimAwareWrapper> CACHE_IDX = ThreadCache.obtainIndex(TrimAwareWrapper.class,
47: Integer.getInteger(ByteBufferManager.class.getName() + ".taw-cache-size", 2));
48:
49: private final ThreadCache.CachedTypeIndex<SmallByteBufferWrapper> SMALL_BUFFER_CACHE_IDX = ThreadCache.obtainIndex(
50: SmallByteBufferWrapper.class.getName() + '.' + System.identityHashCode(this), SmallByteBufferWrapper.class,
51: Integer.getInteger(ByteBufferManager.class.getName() + ".sbbw-cache-size", 16));
52:
53: /**
54: * Is direct ByteBuffer should be used?
55: */
56: protected boolean isDirect;
57:
58: protected final int maxSmallBufferSize;
59:
60: public ByteBufferManager() {
61: this(false, DEFAULT_MAX_BUFFER_SIZE, DEFAULT_SMALL_BUFFER_SIZE);
62: }
63:
64: public ByteBufferManager(final boolean isDirect) {
65: this(isDirect, DEFAULT_MAX_BUFFER_SIZE, DEFAULT_SMALL_BUFFER_SIZE);
66: }
67:
68: public ByteBufferManager(final boolean isDirect, final int maxBufferSize, final int maxSmallBufferSize) {
69: super(maxBufferSize);
70: this.maxSmallBufferSize = maxSmallBufferSize;
71: this.isDirect = isDirect;
72: }
73:
74: public int getMaxSmallBufferSize() {
75: return maxSmallBufferSize;
76: }
77:
78: /**
79: * {@inheritDoc}
80: */
81: @Override
82: public ByteBufferWrapper allocate(final int size) {
83: if (size <= maxSmallBufferSize) {
84: final SmallByteBufferWrapper buffer = createSmallBuffer();
85: buffer.limit(size);
86: return buffer;
87: }
88: return wrap(allocateByteBuffer(size));
89: }
90:
91: /**
92: * {@inheritDoc}
93: */
94: @Override
95: public ByteBufferWrapper allocateAtLeast(int size) {
96: if (size <= maxSmallBufferSize) {
97: final SmallByteBufferWrapper buffer = createSmallBuffer();
98: buffer.limit(size);
99: return buffer;
100: }
101: return wrap(allocateByteBufferAtLeast(size));
102: }
103:
104: /**
105: * {@inheritDoc}
106: */
107: @Override
108: public ByteBufferWrapper reallocate(ByteBufferWrapper oldBuffer, int newSize) {
109: return wrap(reallocateByteBuffer(oldBuffer.underlying(), newSize));
110: }
111:
112: /**
113: * Lets JVM Garbage collector to release buffer.
114: */
115: @Override
116: public void release(ByteBufferWrapper buffer) {
117: releaseByteBuffer(buffer.underlying());
118: }
119:
120: /**
121: * Returns <tt>true</tt>, if <tt>ByteBufferManager</tt> works with direct {@link ByteBuffer}s, or <tt>false</tt>
122: * otherwise.
123: *
124: * @return <tt>true</tt>, if <tt>ByteBufferManager</tt> works with direct {@link ByteBuffer}s, or <tt>false</tt>
125: * otherwise.
126: */
127: public boolean isDirect() {
128: return isDirect;
129: }
130:
131: /**
132: * Set <tt>true</tt>, if <tt>ByteBufferManager</tt> works with direct {@link ByteBuffer}s, or <tt>false</tt> otherwise.
133: *
134: * @param isDirect <tt>true</tt>, if <tt>ByteBufferManager</tt> works with direct {@link ByteBuffer}s, or <tt>false</tt>
135: * otherwise.
136: */
137: public void setDirect(boolean isDirect) {
138: this.isDirect = isDirect;
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: @Override
145: public boolean willAllocateDirect(int size) {
146: return isDirect;
147: }
148:
149: /**
150: * {@inheritDoc}
151: */
152: @Override
153: public ByteBufferWrapper wrap(byte[] data) {
154: return wrap(data, 0, data.length);
155: }
156:
157: /**
158: * {@inheritDoc}
159: */
160: @Override
161: public ByteBufferWrapper wrap(byte[] data, int offset, int length) {
162: return wrap(ByteBuffer.wrap(data, offset, length));
163: }
164:
165: /**
166: * {@inheritDoc}
167: */
168: @Override
169: public ByteBufferWrapper wrap(String s) {
170: return wrap(s, Charset.defaultCharset());
171: }
172:
173: /**
174: * {@inheritDoc}
175: */
176: @Override
177: public ByteBufferWrapper wrap(String s, Charset charset) {
178: try {
179: byte[] byteRepresentation = s.getBytes(charset.name());
180: return wrap(ByteBuffer.wrap(byteRepresentation));
181: } catch (UnsupportedEncodingException e) {
182: throw new IllegalStateException(e);
183: }
184: }
185:
186: @Override
187: public ThreadLocalPool createThreadLocalPool() {
188: return new ByteBufferThreadLocalPool();
189: }
190:
191: /**
192: * {@inheritDoc}
193: */
194: @Override
195: public ByteBufferWrapper wrap(final ByteBuffer byteBuffer) {
196: return createTrimAwareBuffer(byteBuffer);
197: }
198:
199: /**
200: * Allocates {@link ByteBuffer} of required size.
201: *
202: * @param size {@link ByteBuffer} size.
203: * @return allocated {@link ByteBuffer}.
204: */
205: @Override
206: @SuppressWarnings("unchecked")
207: public ByteBuffer allocateByteBuffer(final int size) {
208: if (size > maxBufferSize) {
209: // Don't use pool
210: return allocateByteBuffer0(size);
211: }
212:
213: final ThreadLocalPool<ByteBuffer> threadLocalCache = getByteBufferThreadLocalPool();
214: if (threadLocalCache != null) {
215: final int remaining = threadLocalCache.remaining();
216:
217: if (remaining == 0 || remaining < size) {
218: reallocatePoolBuffer();
219: }
220:
221: return (ByteBuffer) allocateFromPool(threadLocalCache, size);
222: } else {
223: return allocateByteBuffer0(size);
224: }
225:
226: }
227:
228: /**
229: * Allocates {@link ByteBuffer} of required size.
230: *
231: * @param size {@link ByteBuffer} size.
232: * @return allocated {@link ByteBuffer}.
233: */
234: @Override
235: @SuppressWarnings("unchecked")
236: public ByteBuffer allocateByteBufferAtLeast(final int size) {
237: if (size > maxBufferSize) {
238: // Don't use pool
239: return allocateByteBuffer0(size);
240: }
241:
242: final ThreadLocalPool<ByteBuffer> threadLocalCache = getByteBufferThreadLocalPool();
243: if (threadLocalCache != null) {
244: int remaining = threadLocalCache.remaining();
245:
246: if (remaining == 0 || remaining < size) {
247: reallocatePoolBuffer();
248: remaining = threadLocalCache.remaining();
249: }
250:
251: return (ByteBuffer) allocateFromPool(threadLocalCache, remaining);
252: } else {
253: return allocateByteBuffer0(size);
254: }
255: }
256:
257: @Override
258: @SuppressWarnings("unchecked")
259: public ByteBuffer reallocateByteBuffer(ByteBuffer oldByteBuffer, int newSize) {
260: if (oldByteBuffer.capacity() >= newSize) {
261: return oldByteBuffer;
262: }
263:
264: final ThreadLocalPool<ByteBuffer> memoryPool = getByteBufferThreadLocalPool();
265: if (memoryPool != null) {
266: final ByteBuffer newBuffer = memoryPool.reallocate(oldByteBuffer, newSize);
267:
268: if (newBuffer != null) {
269: ProbeNotifier.notifyBufferAllocatedFromPool(monitoringConfig, newSize - oldByteBuffer.capacity());
270:
271: return newBuffer;
272: }
273: }
274: ByteBuffer newByteBuffer = allocateByteBuffer(newSize);
275: oldByteBuffer.flip();
276: return newByteBuffer.put(oldByteBuffer);
277: }
278:
279: @Override
280: @SuppressWarnings("unchecked")
281: public void releaseByteBuffer(ByteBuffer byteBuffer) {
282: ThreadLocalPool<ByteBuffer> memoryPool = getByteBufferThreadLocalPool();
283: if (memoryPool != null) {
284:
285: if (memoryPool.release((ByteBuffer) byteBuffer.clear())) {
286: ProbeNotifier.notifyBufferReleasedToPool(monitoringConfig, byteBuffer.capacity());
287: }
288: }
289:
290: }
291:
292: protected SmallByteBufferWrapper createSmallBuffer() {
293: final SmallByteBufferWrapper buffer = ThreadCache.takeFromCache(SMALL_BUFFER_CACHE_IDX);
294: if (buffer != null) {
295: ProbeNotifier.notifyBufferAllocatedFromPool(monitoringConfig, maxSmallBufferSize);
296: return buffer;
297: }
298:
299: return new SmallByteBufferWrapper(allocateByteBuffer0(maxSmallBufferSize));
300: }
301:
302: // ------- Monitoring section ----------------------
303:
304: @Override
305: public MonitoringConfig<MemoryProbe> getMonitoringConfig() {
306: return monitoringConfig;
307: }
308:
309: /**
310: * Create the Memory Manager JMX management object.
311: *
312: * @return the Memory Manager JMX management object.
313: */
314: @Override
315: protected Object createJmxManagementObject() {
316: return MonitoringUtils.loadJmxObject("org.glassfish.grizzly.memory.jmx.ByteBufferManager", this, ByteBufferManager.class);
317: }
318:
319: protected final ByteBuffer allocateByteBuffer0(final int size) {
320:
321: ProbeNotifier.notifyBufferAllocated(monitoringConfig, size);
322: if (isDirect) {
323: return ByteBuffer.allocateDirect(size);
324: } else {
325: return ByteBuffer.allocate(size);
326: }
327: }
328:
329: private TrimAwareWrapper createTrimAwareBuffer(final ByteBuffer underlyingByteBuffer) {
330:
331: final TrimAwareWrapper buffer = ThreadCache.takeFromCache(CACHE_IDX);
332: if (buffer != null) {
333: buffer.visible = underlyingByteBuffer;
334: return buffer;
335: }
336:
337: return new TrimAwareWrapper(underlyingByteBuffer);
338: }
339:
340: @SuppressWarnings({ "unchecked" })
341: private void reallocatePoolBuffer() {
342: final ByteBuffer byteBuffer = allocateByteBuffer0(maxBufferSize);
343:
344: final ThreadLocalPool<ByteBuffer> threadLocalCache = getByteBufferThreadLocalPool();
345: if (threadLocalCache != null) {
346: threadLocalCache.reset(byteBuffer);
347: }
348: }
349:
350: @SuppressWarnings("unchecked")
351: private static ByteBufferThreadLocalPool getByteBufferThreadLocalPool() {
352: final ThreadLocalPool pool = getThreadLocalPool();
353: return pool instanceof ByteBufferThreadLocalPool ? (ByteBufferThreadLocalPool) pool : null;
354: }
355:
356: // ---------------------------------------------------------- Nested Classes
357:
358: /**
359: * Information about thread associated memory pool.
360: */
361: private static final class ByteBufferThreadLocalPool implements ThreadLocalPool<ByteBuffer> {
362: /**
363: * Memory pool
364: */
365: private ByteBuffer pool;
366:
367: /**
368: * {@link ByteBuffer} allocation history.
369: */
370: private Object[] allocationHistory;
371: private int lastAllocatedIndex;
372:
373: public ByteBufferThreadLocalPool() {
374: allocationHistory = new Object[8];
375: }
376:
377: @Override
378: public void reset(ByteBuffer pool) {
379: Arrays.fill(allocationHistory, 0, lastAllocatedIndex, null);
380: lastAllocatedIndex = 0;
381: this.pool = pool;
382: }
383:
384: @Override
385: public ByteBuffer allocate(int size) {
386: final ByteBuffer allocated = Buffers.slice(pool, size);
387: return addHistory(allocated);
388: }
389:
390: @Override
391: public ByteBuffer reallocate(ByteBuffer oldByteBuffer, int newSize) {
392: if (isLastAllocated(oldByteBuffer) && remaining() + oldByteBuffer.capacity() >= newSize) {
393:
394: lastAllocatedIndex--;
395:
396: pool.position(pool.position() - oldByteBuffer.capacity());
397: final ByteBuffer newByteBuffer = Buffers.slice(pool, newSize);
398: newByteBuffer.position(oldByteBuffer.position());
399:
400: return addHistory(newByteBuffer);
401: }
402:
403: return null;
404: }
405:
406: @Override
407: public boolean release(ByteBuffer underlyingBuffer) {
408: if (isLastAllocated(underlyingBuffer)) {
409: pool.position(pool.position() - underlyingBuffer.capacity());
410: allocationHistory[--lastAllocatedIndex] = null;
411:
412: return true;
413: } else if (wantReset(underlyingBuffer.capacity())) {
414: reset(underlyingBuffer);
415: return true;
416: }
417:
418: return false;
419: }
420:
421: @Override
422: public boolean wantReset(int size) {
423: return !hasRemaining() || lastAllocatedIndex == 0 && pool.remaining() < size;
424: }
425:
426: @Override
427: public boolean isLastAllocated(ByteBuffer oldByteBuffer) {
428: return lastAllocatedIndex > 0 && allocationHistory[lastAllocatedIndex - 1] == oldByteBuffer;
429: }
430:
431: @Override
432: public ByteBuffer reduceLastAllocated(ByteBuffer byteBuffer) {
433: final ByteBuffer oldLastAllocated = (ByteBuffer) allocationHistory[lastAllocatedIndex - 1];
434:
435: pool.position(pool.position() - (oldLastAllocated.capacity() - byteBuffer.capacity()));
436: allocationHistory[lastAllocatedIndex - 1] = byteBuffer;
437:
438: return oldLastAllocated;
439: }
440:
441: @Override
442: public int remaining() {
443: return pool != null ? pool.remaining() : 0;
444: }
445:
446: @Override
447: public boolean hasRemaining() {
448: return remaining() > 0;
449: }
450:
451: private ByteBuffer addHistory(ByteBuffer allocated) {
452: if (lastAllocatedIndex >= allocationHistory.length) {
453: allocationHistory = Arrays.copyOf(allocationHistory, allocationHistory.length * 3 / 2 + 1);
454: }
455:
456: allocationHistory[lastAllocatedIndex++] = allocated;
457: return allocated;
458: }
459:
460: @Override
461: public String toString() {
462: return "(pool=" + pool + " last-allocated-index=" + (lastAllocatedIndex - 1) + " allocation-history=" + Arrays.toString(allocationHistory) + ')';
463: }
464:
465: } // END ByteBufferThreadLocalPool
466:
467: /**
468: * {@link ByteBufferWrapper} implementation, which supports trimming. In other words it's possible to return unused
469: * {@link org.glassfish.grizzly.Buffer} space to pool.
470: */
471: private final class TrimAwareWrapper extends ByteBufferWrapper implements TrimAware {
472:
473: private TrimAwareWrapper(ByteBuffer underlyingByteBuffer) {
474: super(underlyingByteBuffer);
475: }
476:
477: @Override
478: @SuppressWarnings("unchecked")
479: public void trim() {
480: final int sizeToReturn = visible.capacity() - visible.position();
481:
482: if (sizeToReturn > 0) {
483: final ThreadLocalPool<ByteBuffer> threadLocalCache = getByteBufferThreadLocalPool();
484: if (threadLocalCache != null) {
485:
486: if (threadLocalCache.isLastAllocated(visible)) {
487: visible.flip();
488:
489: visible = visible.slice();
490: threadLocalCache.reduceLastAllocated(visible);
491:
492: return;
493: } else if (threadLocalCache.wantReset(sizeToReturn)) {
494: visible.flip();
495:
496: final ByteBuffer originalByteBuffer = visible;
497: visible = visible.slice();
498: originalByteBuffer.position(originalByteBuffer.limit());
499: originalByteBuffer.limit(originalByteBuffer.capacity());
500:
501: threadLocalCache.reset(originalByteBuffer);
502: return;
503: }
504: }
505: }
506:
507: super.trim();
508: }
509:
510: @Override
511: public void recycle() {
512: allowBufferDispose = false;
513:
514: ThreadCache.putToCache(CACHE_IDX, this);
515: }
516:
517: @Override
518: public void dispose() {
519: prepareDispose();
520: ByteBufferManager.this.release(this);
521: visible = null;
522: recycle();
523: }
524:
525: @Override
526: protected ByteBufferWrapper wrapByteBuffer(ByteBuffer byteBuffer) {
527: return ByteBufferManager.this.wrap(byteBuffer);
528: }
529:
530: } // END TrimAwareWrapper
531:
532: /**
533: * {@link ByteBufferWrapper} implementation, which supports trimming. In other words it's possible to return unused
534: * {@link org.glassfish.grizzly.Buffer} space to pool.
535: */
536: protected final class SmallByteBufferWrapper extends ByteBufferWrapper implements Cacheable {
537:
538: private SmallByteBufferWrapper(ByteBuffer underlyingByteBuffer) {
539: super(underlyingByteBuffer);
540: }
541:
542: @Override
543: public void dispose() {
544: super.prepareDispose();
545: visible.clear();
546: recycle();
547: }
548:
549: @Override
550: public void recycle() {
551:• if (visible.remaining() == maxSmallBufferSize) {
552: allowBufferDispose = false;
553: disposeStackTrace = null;
554:
555:• if (ThreadCache.putToCache(SMALL_BUFFER_CACHE_IDX, this)) {
556: ProbeNotifier.notifyBufferReleasedToPool(monitoringConfig, maxSmallBufferSize);
557: }
558: }
559: }
560:
561: @Override
562: protected ByteBufferWrapper wrapByteBuffer(final ByteBuffer byteBuffer) {
563: return ByteBufferManager.this.wrap(byteBuffer);
564: }
565:
566: } // END SmallByteBufferWrapper
567:
568: }