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

com.sun.grizzly.memory.DefaultMemoryManager Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.grizzly.memory;

import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;
import com.sun.grizzly.threadpool.DefaultWorkerThread;

/**
 * Default {@link MemoryManager}, used in Grizzly.
 * DefaultMemory has simple {@link Buffer} pooling implementation,
 * which makes released {@link Buffer}'s memory to be reused.
 *
 * @author Alexey Stashok
 */
public class DefaultMemoryManager extends ByteBufferViewManager {
    public static final int DEFAULT_MAX_BUFFER_SIZE = 1024 * 128;
    
    /**
     * Max size of memory pool for one thread.
     */
    private int maxThreadBufferSize = DEFAULT_MAX_BUFFER_SIZE;

    private boolean isMonitoring;

    /**
     * Real amount of bytes, which was allocated.
     */
    private AtomicLong totalBytesAllocated = new AtomicLong();
    
    /**
     * Get the maximum size of memory pool for one thread.
     *
     * @return the maximum size of memory pool for one thread.
     */
    public int getMaxThreadBufferSize() {
        return maxThreadBufferSize;
    }

    /**
     * Set the maximum size of memory pool for one thread.
     *
     * @param maxThreadBufferSize the maximum size of memory pool for one thread.
     */
    public void setMaxThreadBufferSize(int maxThreadBufferSize) {
        this.maxThreadBufferSize = maxThreadBufferSize;
    }

    /**
     * Is monotoring enabled.
     * 
     * @return true, if monitoring is enabled, or false
     * otherwise.
     */
    public boolean isMonitoring() {
        return isMonitoring;
    }

    /**
     * Set monotoring mode.
     *
     * @param isMonitoring true, if monitoring is enabled, or
     * false otherwise.
     */
    public void setMonitoring(boolean isMonitoring) {
        this.isMonitoring = isMonitoring;
        ByteBufferWrapper.DEBUG_MODE = isMonitoring;
    }

    /**
     * Get real number of bytes allocated by this {@link MemoryManager}.
     * It doesn't count bytes, which were pooled and then reused.
     * 
     * @return real number of bytes allocated by this {@link MemoryManager}.
     */
    public long getTotalBytesAllocated() {
        return totalBytesAllocated.get();
    }

    /**
     * Allocates {@link Buffer} of required size.
     * First of all DefaultMemoryManager tries to reuse thread local
     * memory pool. If it's not possible - it delegates allocation to 
     * {@link ByteBufferViewManager}.
     * 
     * @param size number of bytes to be allocated.
     * 
     * @return allocated {@link ByteBufferWrapper}.
     */
    @Override
    public ByteBufferWrapper allocate(int size) {
        if (isDefaultWorkerThread()) {
            BufferInfo bufferInfo = getThreadBuffer();

            if (bufferInfo == null || bufferInfo.buffer == null) {
                return incAllocated(super.allocate(size));
            }

            ByteBufferWrapper allocatedFromPool = allocateFromPool(bufferInfo, size);

            if (allocatedFromPool != null) {
                return allocatedFromPool;
            } else {
                return incAllocated(super.allocate(size));
            }

        } else {
            return incAllocated(super.allocate(size));
        }
    }

    /**
     * Reallocate {@link Buffer} to a required size.
     * First of all DefaultMemoryManager tries to reuse thread local
     * memory pool. If it's not possible - it delegates reallocation to
     * {@link ByteBufferViewManager}.
     *
     * @param oldBuffer old {@link Buffer} we want to reallocate.
     * @param newSize {@link Buffer} required size.
     *
     * @return reallocated {@link Buffer}.
     */

    @Override
    public ByteBufferWrapper reallocate(ByteBufferWrapper oldBuffer,
            int newSize) {
        if (oldBuffer.capacity() <= newSize) return oldBuffer;
        
        if (isDefaultWorkerThread()) {
            final BufferInfo bufferInfo = getThreadBuffer();
            if (bufferInfo != null &&
                    bufferInfo.buffer != null &&
                    bufferInfo.lastAllocatedBuffer == oldBuffer.visible &&
                    bufferInfo.buffer.remaining() + oldBuffer.capacity() >= newSize) {
                final ByteBuffer bufferPool = bufferInfo.buffer;
                bufferPool.position(bufferPool.position() - oldBuffer.capacity());

                return allocateFromPool(bufferInfo, newSize);
            }
        }

        return incAllocated(super.reallocate(oldBuffer, newSize));
    }

    private ByteBufferWrapper allocateFromPool(final BufferInfo bufferInfo,
            final int size) {
        if (bufferInfo == null || bufferInfo.buffer == null) return null;
        final ByteBuffer bufferPool = bufferInfo.buffer;
        if (bufferPool.remaining() >= size) {
            ByteBuffer allocatedByteBuffer;

            if (bufferPool.position() == 0) {
                allocatedByteBuffer = bufferPool;
                bufferInfo.buffer = null;
            } else {
                allocatedByteBuffer = slice(bufferPool, size);
            }

            bufferInfo.lastAllocatedBuffer = allocatedByteBuffer;
            return wrap(allocatedByteBuffer);
        }

        return null;
    }


    /**
     * Release {@link Buffer}.
     * DefaultMemoryManager will checks if it's possible to return
     * the buffer to thread local pool. If not - let's garbage collector utilize
     * the memory.
     *
     * @param buffer {@link Buffer} to be released.
     */
    @Override
    public void release(ByteBufferWrapper buffer) {
        if (isDefaultWorkerThread()) {
            ByteBuffer underlyingBuffer = buffer.underlying();
            
            BufferInfo bufferInfo = getThreadBuffer();

            if (prepend(bufferInfo, underlyingBuffer)) return;

            if (bufferInfo == null || bufferInfo.buffer == null ||
                    (bufferInfo.buffer.capacity() <= underlyingBuffer.capacity() &&
                    underlyingBuffer.capacity() <= maxThreadBufferSize)) {

                boolean isNewBufferInfo = false;
                if (bufferInfo == null) {
                    bufferInfo = new BufferInfo();
                    isNewBufferInfo = true;
                }
                
                underlyingBuffer.clear();
                bufferInfo.buffer = underlyingBuffer;
                bufferInfo.lastAllocatedBuffer = null;

                if (isNewBufferInfo) {
                    setThreadBuffer(bufferInfo);
                }
                
                return;
            }

        }
        super.release(buffer);
    }

    /**
     * Get the size of local thread memory pool.
     * 
     * @return the size of local thread memory pool.
     */
    public int getReadyThreadBufferSize() {
        if (isDefaultWorkerThread()) {
            BufferInfo bi = getThreadBuffer();
            if (bi != null && bi.buffer != null) {
                return bi.buffer.remaining();
            }
        }

        return 0;
    }


    @Override
    public ByteBufferWrapper wrap(ByteBuffer byteBuffer) {
        return new TrimAwareWrapper(this, byteBuffer);
    }

    /**
     * Get thread associated memory pool info.
     * 
     * @return thread associated memory pool info.
     */
    private BufferInfo getThreadBuffer() {
        DefaultWorkerThread workerThread =
                (DefaultWorkerThread) Thread.currentThread();
        return workerThread.getAssociatedBuffer();
    }

    /**
     * Set thread associated memory pool info.
     * 
     * @param bufferInfo thread associated memory pool info.
     */
    private void setThreadBuffer(BufferInfo bufferInfo) {
        DefaultWorkerThread workerThread =
                (DefaultWorkerThread) Thread.currentThread();
        workerThread.setAssociatedBuffer(bufferInfo);
    }

    /**
     * Counts total allocated memory size.
     */
    private ByteBufferWrapper incAllocated(ByteBufferWrapper allocated) {
        if (isMonitoring) {
            totalBytesAllocated.addAndGet(allocated.capacity());
        }

        return allocated;
    }

    /**
     * If possible, prepends passed underlyingBuffer to thread local memory
     * pool.
     * 
     * @param bufferInfo thread associated memory pool info.
     * @param underlyingBuffer {@link ByteBuffer} to prepend
     * @return true, if {@link ByteBuffer} was prepended,
     * or false otherwise.
     */
    private boolean prepend(BufferInfo bufferInfo, ByteBuffer underlyingBuffer) {
        if (bufferInfo != null &&
                bufferInfo.lastAllocatedBuffer == underlyingBuffer) {
            bufferInfo.lastAllocatedBuffer = null;
            if (bufferInfo.buffer != null) {
                ByteBuffer chunk = bufferInfo.buffer;
                chunk.position(chunk.position() - underlyingBuffer.capacity());
            } else {
                bufferInfo.buffer = underlyingBuffer;
            }

            return true;

        }

        return false;
    }

    private boolean isDefaultWorkerThread() {
        return Thread.currentThread() instanceof DefaultWorkerThread;
    }

    /**
     * Information about thread associated memory pool.
     */
    public class BufferInfo {
        /**
         * Memory pool
         */
        public ByteBuffer buffer;

        /**
         * Last allocated {@link ByteBuffer}.
         */
        public ByteBuffer lastAllocatedBuffer;

        @Override
        public String toString() {
            return "(buffer=" + buffer + " lastAllocatedBuffer=" + lastAllocatedBuffer + ")";
        }


    }

    /**
     * {@link ByteBufferWrapper} implementation, which supports triming. In
     * other words it's possible to return unused {@link Buffer} space to
     * pool.
     */
    public class TrimAwareWrapper extends ByteBufferWrapper {

        public TrimAwareWrapper(ByteBufferManager memoryManager,
                ByteBuffer underlyingByteBuffer) {
            super(memoryManager, underlyingByteBuffer);
        }

        @Override
        public void trim() {
            int sizeToReturn = visible.capacity() - visible.position();

            BufferInfo bufferInfo;
            
            if (sizeToReturn > 0 && isDefaultWorkerThread() &&
                    ((bufferInfo = getThreadBuffer()) == null
                    || bufferInfo.lastAllocatedBuffer == visible)) {
                visible.flip();

                ByteBuffer originalByteBuffer = visible;
                visible = visible.slice();
                if (bufferInfo == null) {
                    originalByteBuffer.position(originalByteBuffer.limit());
                    originalByteBuffer.limit(originalByteBuffer.capacity());
                    bufferInfo = new BufferInfo();
                    bufferInfo.buffer = originalByteBuffer;
                    bufferInfo.lastAllocatedBuffer = visible;
                    setThreadBuffer(bufferInfo);

                } else if (bufferInfo.lastAllocatedBuffer == originalByteBuffer) {
                    if (bufferInfo.buffer == null) {
                        originalByteBuffer.position(originalByteBuffer.limit());
                        originalByteBuffer.limit(originalByteBuffer.capacity());
                        bufferInfo.buffer = originalByteBuffer;
                    } else {
                        bufferInfo.buffer.position(
                                bufferInfo.buffer.position() - sizeToReturn);
                    }
                    bufferInfo.lastAllocatedBuffer = visible;
                }
            } else {
                super.trim();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy