All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.grizzly.memory.HeapMemoryManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

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;

/**
 * A {@link WrapperAware} {@link MemoryManager} implementation for managing {@link HeapBuffer} instances.
 *
 * @since 2.0
 */
public class HeapMemoryManager extends AbstractMemoryManager implements WrapperAware {

    private static final ThreadCache.CachedTypeIndex CACHE_IDX = ThreadCache.obtainIndex(TrimmableHeapBuffer.class,
            Integer.getInteger(HeapMemoryManager.class.getName() + ".thb-cache-size", 8));

    private static final ThreadCache.CachedTypeIndex 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);
    }

    // ---------------------------------------------- Methods from MemoryManager

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer allocate(final int size) {
        return allocateHeapBuffer(size);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer allocateAtLeast(final int size) {
        return allocateHeapBufferAtLeast(size);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer reallocate(final HeapBuffer oldBuffer, final int newSize) {
        return reallocateHeapBuffer(oldBuffer, newSize);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void release(final HeapBuffer buffer) {
        releaseHeapBuffer(buffer);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean willAllocateDirect(int size) {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MonitoringConfig getMonitoringConfig() {
        return monitoringConfig;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ThreadLocalPool createThreadLocalPool() {
        return new HeapBufferThreadLocalPool(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object createJmxManagementObject() {
        return MonitoringUtils.loadJmxObject("org.glassfish.grizzly.memory.jmx.HeapMemoryManager", this, HeapMemoryManager.class);
    }

    // ----------------------------------------------- Methods from WrapperAware

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer wrap(final byte[] data) {
        return createTrimAwareBuffer(data, 0, data.length);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer wrap(final byte[] data, final int offset, final int length) {
        return createTrimAwareBuffer(data, offset, length);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer wrap(final String s) {
        return wrap(s, Charset.defaultCharset());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HeapBuffer wrap(final String s, final Charset charset) {
        return wrap(s.getBytes(charset));
    }

    /**
     * {@inheritDoc}
     */
    @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 Methods

    /**
     * Creates a new HeapBuffer with a a specified size.
     * 
     * @param size size of buffer created
     * @return
     */
    protected HeapBuffer allocateHeapBuffer(final int size) {
        if (size > maxBufferSize) {
            // Don't use pool
            return createTrimAwareBuffer(size);
        }

        final ThreadLocalPool 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) {
            // Don't use pool
            return createTrimAwareBuffer(size);
        }

        final ThreadLocalPool 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 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 memoryPool = getHeapBufferThreadLocalPool();
        if (memoryPool != null) {

            if (memoryPool.release(heapBuffer.clear())) {
                ProbeNotifier.notifyBufferReleasedToPool(monitoringConfig, heapBuffer.capacity());
            }
        }

    }

    // --------------------------------------------------------- Private Methods

    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);
    }

    /**
     * Gets the thread local buffer pool If the pool is not an instance of HeapBufferThreadLocalPool then null is returned
     * 
     * @return
     */
    @SuppressWarnings("unchecked")
    private static HeapBufferThreadLocalPool getHeapBufferThreadLocalPool() {
        final ThreadLocalPool pool = getThreadLocalPool();
        return pool instanceof HeapBufferThreadLocalPool ? (HeapBufferThreadLocalPool) pool : null;
    }

    // ---------------------------------------------------------- Nested Classes

    /**
     * Information about thread associated memory pool.
     */
    private static final class HeapBufferThreadLocalPool implements ThreadLocalPool {
        /**
         * Memory pool
         */
        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;
        }

    } // END ByteBufferThreadLocalPool

    /**
     * {@link HeapBuffer} implementation, which supports trimming. In other words it's possible to return unused
     * {@link org.glassfish.grizzly.Buffer} space to pool.
     */
    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;
        }
    } // END TrimAwareWrapper

    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;
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy