package org.glassfish.grizzly.memory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.monitoring.MonitoringConfig;
import org.glassfish.grizzly.monitoring.MonitoringUtils;
public class HeapMemoryManager extends AbstractMemoryManager<HeapBuffer> implements WrapperAware {
private static final ThreadCache.CachedTypeIndex<TrimmableHeapBuffer> CACHE_IDX = ThreadCache.obtainIndex(TrimmableHeapBuffer.class,
Integer.getInteger(HeapMemoryManager.class.getName() + ".thb-cache-size", 8));
private static final ThreadCache.CachedTypeIndex<RecyclableByteBufferWrapper> BBW_CACHE_IDX = ThreadCache.obtainIndex(RecyclableByteBufferWrapper.class,
Integer.getInteger(HeapMemoryManager.class.getName() + ".rbbw-cache-size", 2));
public HeapMemoryManager() {
super();
}
public HeapMemoryManager(final int maxBufferSize) {
super(maxBufferSize);
}
@Override
public HeapBuffer allocate(final int size) {
return allocateHeapBuffer(size);
}
@Override
public HeapBuffer allocateAtLeast(final int size) {
return allocateHeapBufferAtLeast(size);
}
@Override
public HeapBuffer reallocate(final HeapBuffer oldBuffer, final int newSize) {
return reallocateHeapBuffer(oldBuffer, newSize);
}
@Override
public void release(final HeapBuffer buffer) {
releaseHeapBuffer(buffer);
}
@Override
public boolean willAllocateDirect(int size) {
return false;
}
@Override
public MonitoringConfig<MemoryProbe> getMonitoringConfig() {
return monitoringConfig;
}
@Override
public ThreadLocalPool createThreadLocalPool() {
return new HeapBufferThreadLocalPool(this);
}
@Override
protected Object createJmxManagementObject() {
return MonitoringUtils.loadJmxObject("org.glassfish.grizzly.memory.jmx.HeapMemoryManager", this, HeapMemoryManager.class);
}
@Override
public HeapBuffer wrap(final byte[] data) {
return createTrimAwareBuffer(data, 0, data.length);
}
@Override
public HeapBuffer wrap(final byte[] data, final int offset, final int length) {
return createTrimAwareBuffer(data, offset, length);
}
@Override
public HeapBuffer wrap(final String s) {
return wrap(s, Charset.defaultCharset());
}
@Override
public HeapBuffer wrap(final String s, final Charset charset) {
return wrap(s.getBytes(charset));
}
@Override
public Buffer wrap(final ByteBuffer byteBuffer) {
if (byteBuffer.hasArray()) {
return wrap(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), byteBuffer.remaining());
} else {
return createByteBufferWrapper(byteBuffer);
}
}
protected HeapBuffer allocateHeapBuffer(final int size) {
if (size > maxBufferSize) {
return createTrimAwareBuffer(size);
}
final ThreadLocalPool<HeapBuffer> threadLocalCache = getHeapBufferThreadLocalPool();
if (threadLocalCache != null) {
final int remaining = threadLocalCache.remaining();
if (remaining == 0 || remaining < size) {
reallocatePoolBuffer();
}
return (HeapBuffer) allocateFromPool(threadLocalCache, size);
} else {
return createTrimAwareBuffer(size);
}
}
protected HeapBuffer allocateHeapBufferAtLeast(final int size) {
if (size > maxBufferSize) {
return createTrimAwareBuffer(size);
}
final ThreadLocalPool<HeapBuffer> threadLocalCache = getHeapBufferThreadLocalPool();
if (threadLocalCache != null) {
int remaining = threadLocalCache.remaining();
if (remaining == 0 || remaining < size) {
reallocatePoolBuffer();
remaining = threadLocalCache.remaining();
}
return (HeapBuffer) allocateFromPool(threadLocalCache, remaining);
} else {
return createTrimAwareBuffer(size);
}
}
protected HeapBuffer reallocateHeapBuffer(HeapBuffer oldHeapBuffer, int newSize) {
if (oldHeapBuffer.capacity() >= newSize) {
return oldHeapBuffer;
}
final ThreadLocalPool<HeapBuffer> memoryPool = getHeapBufferThreadLocalPool();
if (memoryPool != null) {
final HeapBuffer newBuffer = memoryPool.reallocate(oldHeapBuffer, newSize);
if (newBuffer != null) {
ProbeNotifier.notifyBufferAllocatedFromPool(monitoringConfig, newSize - oldHeapBuffer.capacity());
return newBuffer;
}
}
final HeapBuffer newHeapBuffer = allocateHeapBuffer(newSize);
oldHeapBuffer.flip();
return newHeapBuffer.put(oldHeapBuffer);
}
protected final void releaseHeapBuffer(final HeapBuffer heapBuffer) {
final ThreadLocalPool<HeapBuffer> memoryPool = getHeapBufferThreadLocalPool();
if (memoryPool != null) {
if (memoryPool.release(heapBuffer.clear())) {
ProbeNotifier.notifyBufferReleasedToPool(monitoringConfig, heapBuffer.capacity());
}
}
}
private void reallocatePoolBuffer() {
final byte[] heap = new byte[maxBufferSize];
ProbeNotifier.notifyBufferAllocated(monitoringConfig, maxBufferSize);
final HeapBufferThreadLocalPool threadLocalCache = getHeapBufferThreadLocalPool();
if (threadLocalCache != null) {
threadLocalCache.reset(heap, 0, maxBufferSize);
}
}
TrimmableHeapBuffer createTrimAwareBuffer(final int length) {
final byte[] heap = new byte[length];
ProbeNotifier.notifyBufferAllocated(monitoringConfig, length);
return createTrimAwareBuffer(heap, 0, length);
}
TrimmableHeapBuffer createTrimAwareBuffer(final byte[] heap, final int offset, final int length) {
final TrimmableHeapBuffer buffer = ThreadCache.takeFromCache(CACHE_IDX);
if (buffer != null) {
buffer.initialize(this, heap, offset, length);
return buffer;
}
return new TrimmableHeapBuffer(this, heap, offset, length);
}
private ByteBufferWrapper createByteBufferWrapper(final ByteBuffer underlyingByteBuffer) {
final RecyclableByteBufferWrapper buffer = ThreadCache.takeFromCache(BBW_CACHE_IDX);
if (buffer != null) {
buffer.initialize(underlyingByteBuffer);
return buffer;
}
return new RecyclableByteBufferWrapper(underlyingByteBuffer);
}
@SuppressWarnings("unchecked")
private static HeapBufferThreadLocalPool getHeapBufferThreadLocalPool() {
final ThreadLocalPool pool = getThreadLocalPool();
return pool instanceof HeapBufferThreadLocalPool ? (HeapBufferThreadLocalPool) pool : null;
}
private static final class HeapBufferThreadLocalPool implements ThreadLocalPool<HeapBuffer> {
private byte[] pool;
private int leftPos;
private int rightPos;
private int start;
private int end;
private final ByteBuffer[] byteBufferCache;
private int byteBufferCacheSize = 0;
private final HeapMemoryManager mm;
public HeapBufferThreadLocalPool(final HeapMemoryManager mm) {
this(mm, 8);
}
public HeapBufferThreadLocalPool(final HeapMemoryManager mm, final int maxByteBufferCacheSize) {
byteBufferCache = new ByteBuffer[maxByteBufferCacheSize];
this.mm = mm;
}
@Override
public HeapBuffer allocate(final int size) {
final HeapBuffer allocated = mm.createTrimAwareBuffer(pool, rightPos, size);
if (byteBufferCacheSize > 0) {
allocated.byteBuffer = byteBufferCache[--byteBufferCacheSize];
byteBufferCache[byteBufferCacheSize] = null;
}
rightPos += size;
return allocated;
}
@Override
public HeapBuffer reallocate(final HeapBuffer heapBuffer, final int newSize) {
final int diff;
if (isLastAllocated(heapBuffer) && remaining() >= (diff = newSize - heapBuffer.cap)) {
rightPos += diff;
heapBuffer.cap = newSize;
heapBuffer.lim = newSize;
return heapBuffer;
}
return null;
}
@Override
public boolean release(final HeapBuffer heapBuffer) {
boolean canCacheByteBuffer = heapBuffer.byteBuffer != null && byteBufferCacheSize < byteBufferCache.length;
final boolean result;
if (isLastAllocated(heapBuffer)) {
rightPos -= heapBuffer.cap;
if (leftPos == rightPos) {
leftPos = rightPos = start;
}
result = true;
} else if (isReleasableLeft(heapBuffer)) {
leftPos += heapBuffer.cap;
if (leftPos == rightPos) {
leftPos = rightPos = start;
}
result = true;
} else if (wantReset(heapBuffer.cap)) {
reset(heapBuffer);
result = true;
} else {
canCacheByteBuffer = canCacheByteBuffer && pool == heapBuffer.heap;
result = false;
}
if (canCacheByteBuffer) {
byteBufferCache[byteBufferCacheSize++] = heapBuffer.byteBuffer;
}
return result;
}
@Override
public void reset(final HeapBuffer heapBuffer) {
reset(heapBuffer.heap, heapBuffer.offset, heapBuffer.cap);
}
public void reset(final byte[] heap, final int offset, final int capacity) {
if (pool != heap) {
clearByteBufferCache();
pool = heap;
}
leftPos = rightPos = start = offset;
end = offset + capacity;
}
@Override
public boolean wantReset(final int size) {
return size - remaining() > 1024;
}
@Override
public boolean isLastAllocated(final HeapBuffer oldHeapBuffer) {
return oldHeapBuffer.heap == pool && oldHeapBuffer.offset + oldHeapBuffer.cap == rightPos;
}
private boolean isReleasableLeft(final HeapBuffer oldHeapBuffer) {
return oldHeapBuffer.heap == pool && oldHeapBuffer.offset == leftPos;
}
@Override
public HeapBuffer reduceLastAllocated(final HeapBuffer heapBuffer) {
final int newPos = heapBuffer.offset + heapBuffer.cap;
ProbeNotifier.notifyBufferReleasedToPool(mm.monitoringConfig, rightPos - newPos);
rightPos = newPos;
return null;
}
@Override
public int remaining() {
return end - rightPos;
}
@Override
public boolean hasRemaining() {
return rightPos < end;
}
@Override
public String toString() {
return "(pool=" + pool.length + " pos=" + rightPos + " cap=" + end + ')';
}
private void clearByteBufferCache() {
Arrays.fill(byteBufferCache, 0, byteBufferCacheSize, null);
byteBufferCacheSize = 0;
}
}
private static final class TrimmableHeapBuffer extends HeapBuffer implements TrimAware {
private HeapMemoryManager mm;
private TrimmableHeapBuffer(final HeapMemoryManager mm, byte[] heap, int offset, int capacity) {
super(heap, offset, capacity);
this.mm = mm;
}
@Override
public void trim() {
checkDispose();
final int sizeToReturn = cap - pos;
if (sizeToReturn > 0) {
final HeapBufferThreadLocalPool threadLocalCache = getHeapBufferThreadLocalPool();
if (threadLocalCache != null) {
if (threadLocalCache.isLastAllocated(this)) {
flip();
cap = lim;
threadLocalCache.reduceLastAllocated(this);
return;
} else if (threadLocalCache.wantReset(sizeToReturn)) {
flip();
cap = lim;
threadLocalCache.reset(heap, offset + cap, sizeToReturn);
return;
}
}
}
super.trim();
}
@Override
public void recycle() {
allowBufferDispose = false;
ThreadCache.putToCache(CACHE_IDX, this);
}
@Override
public void dispose() {
prepareDispose();
mm.release(this);
mm = null;
byteBuffer = null;
heap = null;
pos = 0;
offset = 0;
lim = 0;
cap = 0;
order = ByteOrder.BIG_ENDIAN;
bigEndian = true;
recycle();
}
@Override
protected HeapBuffer createHeapBuffer(final int offs, final int capacity) {
return mm.createTrimAwareBuffer(heap, offs + offset, capacity);
}
void initialize(final HeapMemoryManager mm, final byte[] heap, final int offset, final int length) {
this.mm = mm;
this.heap = heap;
this.offset = offset;
pos = 0;
cap = length;
lim = length;
disposeStackTrace = null;
}
}
private final static class RecyclableByteBufferWrapper extends ByteBufferWrapper implements Cacheable {
private RecyclableByteBufferWrapper(final ByteBuffer underlyingByteBuffer) {
super(underlyingByteBuffer);
}
@Override
public void recycle() {
allowBufferDispose = false;
ThreadCache.putToCache(BBW_CACHE_IDX, this);
}
@Override
public void dispose() {
super.dispose();
recycle();
}
private void initialize(final ByteBuffer underlyingByteBuffer) {
visible = underlyingByteBuffer;
disposeStackTrace = null;
}
}
}