/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package org.opencloudb.buffer; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.log4j.Logger; /** * @author mycat */ public final class BufferPool { // this value not changed ,isLocalCacheThread use it public static final String LOCAL_BUF_THREAD_PREX = "$_"; private static final ThreadLocalBufferPool localBufferPool = new ThreadLocalBufferPool( 4000); private static final Logger LOGGER = Logger.getLogger(BufferPool.class); private final int chunkSize; private final ConcurrentLinkedQueue<ByteBuffer> items = new ConcurrentLinkedQueue<ByteBuffer>(); private long sharedOptsCount; private volatile int newCreated; private final int threadLocalCount; private final int capactiy; private long totalBytes = 0; private long totalCounts = 0; public BufferPool(int bufferSize, int chunkSize, int threadLocalPercent) { this.chunkSize = chunkSize; int size = bufferSize / chunkSize; size = (bufferSize % chunkSize == 0) ? size : size + 1; this.capactiy = size; threadLocalCount = threadLocalPercent * capactiy / 100; for (int i = 0; i < capactiy; i++) { items.offer(createDirectBuffer(chunkSize)); } } private static final boolean isLocalCacheThread() { final String thname = Thread.currentThread().getName(); return (thname.length() < LOCAL_BUF_THREAD_PREX.length()) ? false : (thname.charAt(0) == '$' && thname.charAt(1) == '_'); } public long getSharedOptsCount() { return sharedOptsCount; } public int size() { return this.items.size(); } public int capacity() { return capactiy + newCreated; } public ByteBuffer allocate() { ByteBuffer node = null; if (isLocalCacheThread()) { // allocate from threadlocal node = localBufferPool.get().poll(); if (node != null) { return node; } } node = items.poll(); if (node == null) { newCreated++; node = this.createDirectBuffer(chunkSize); } return node; } private boolean checkValidBuffer(ByteBuffer buffer) { // 拒绝回收null和容量大于chunkSize的缓存 if (buffer == null || !buffer.isDirect()) { return false; } else if (buffer.capacity() > chunkSize) { LOGGER.warn("cant' recycle a buffer large than my pool chunksize " + buffer.capacity()); return false; } totalCounts++; totalBytes += buffer.limit(); buffer.clear(); return true; } public void recycle(ByteBuffer buffer) { if (!checkValidBuffer(buffer)) { return; } if (isLocalCacheThread()) { BufferQueue localQueue = localBufferPool.get(); if (localQueue.snapshotSize() < threadLocalCount) { localQueue.put(buffer); } else { // recyle 3/4 thread local buffer items.addAll(localQueue.removeItems(threadLocalCount * 3 / 4)); items.offer(buffer); sharedOptsCount++; } } else { sharedOptsCount++; items.offer(buffer); } } public int getAvgBufSize() { if (this.totalBytes < 0) { totalBytes = 0; this.totalCounts = 0; return 0; } else { return (int) (totalBytes / totalCounts); } } public boolean testIfDuplicate(ByteBuffer buffer) { for (ByteBuffer exists : items) { if (exists == buffer) { return true; } } return false; } private ByteBuffer createTempBuffer(int size) { return ByteBuffer.allocate(size); } private ByteBuffer createDirectBuffer(int size) { // for performance return ByteBuffer.allocateDirect(size); } public ByteBuffer allocate(int size) { if (size <= this.chunkSize) { return allocate(); } else { LOGGER.warn("allocate buffer size large than default chunksize:" + this.chunkSize + " he want " + size); return createTempBuffer(size); } } public static void main(String[] args) { BufferPool pool = new BufferPool(1024 * 5, 1024, 2); int i = pool.capacity(); ArrayList<ByteBuffer> all = new ArrayList<ByteBuffer>(); for (int j = 0; j <= i; j++) { all.add(pool.allocate()); } for (ByteBuffer buf : all) { pool.recycle(buf); } System.out.println(pool.size()); } }