📄 directbytebufferpool.java
字号:
/*
* Created on Jan 30, 2004
* Created by Alon Rohter
* Copyright (C) 2004 Alon Rohter, All Rights Reserved.
*
*/
package org.gudy.azureus2.core3.util;
import java.nio.ByteBuffer;
import java.util.*;
import java.math.*;
import org.gudy.azureus2.core3.disk.DiskManager;
import org.gudy.azureus2.core3.logging.LGLogger;
import com.aelitis.azureus.core.diskmanager.cache.*;
/**
* This class handles allocation of direct ByteBuffers.
* It always tries to find a free buffer in the buffer pool
* before creating a new one.
*/
public class
DirectByteBufferPool
{
protected static final boolean DEBUG_TRACK_HANDEDOUT = AEDiagnostics.TRACE_DBB_POOL_USAGE;
protected static final boolean DEBUG_PRINT_MEM = AEDiagnostics.PRINT_DBB_POOL_USAGE;
protected static final int DEBUG_PRINT_TIME = 300*1000;
// There is no point in allocating buffers smaller than 4K,
// as direct ByteBuffers are page-aligned to the underlying
// system, which is 4096 byte pages under most OS's.
// If we want to save memory, we can distribute smaller-than-4K
// buffers by using the slice() method to break up a standard buffer
// into smaller chunks, but that's more work.
private static final int START_POWER = 12; // 4096
private static final int END_POWER = 25; // 33554432
// without an extra bucket here we get lots of wastage with the file cache as typically
// 16K data reads result in a buffer slightly bigger than 16K due to protocol header
// This means we would bump up to 32K pool entries, hence wasting 16K per 16K entry
private static final int[] EXTRA_BUCKETS = { 128, DiskManager.BLOCK_SIZE + 128 };
public static final int MAX_SIZE = BigInteger.valueOf(2).pow(END_POWER).intValue();
private static final DirectByteBufferPool pool = new DirectByteBufferPool();
private final Map buffersMap = new LinkedHashMap(END_POWER - START_POWER + 1);
private final Object poolsLock = new Object();
private final Timer compactionTimer;
private final Map handed_out = new IdentityHashMap(); // for debugging (ByteBuffer has .equals defined on contents
// hence IdentityHashMap)
private static final long COMPACTION_CHECK_PERIOD = 10*60*1000; //10 min
private static final long MAX_FREE_BYTES = 10*1024*1024; //10 MB
private long bytesIn = 0;
private long bytesOut = 0;
private DirectByteBufferPool() {
//create the buffer pool for each buffer size
ArrayList list = new ArrayList();
for (int p=START_POWER; p <= END_POWER; p++) {
list.add( new Integer(BigInteger.valueOf(2).pow(p).intValue()));
}
for (int i=0;i<EXTRA_BUCKETS.length;i++){
list.add( new Integer(EXTRA_BUCKETS[i]));
}
Integer[] sizes = new Integer[ list.size() ];
list.toArray( sizes );
Arrays.sort( sizes);
for (int i=0;i<sizes.length;i++){
ArrayList bufferPool = new ArrayList();
buffersMap.put(sizes[i], bufferPool);
}
//initiate periodic timer to check free memory usage
compactionTimer = new Timer("BufferPool Checker");
compactionTimer.addPeriodicEvent(
COMPACTION_CHECK_PERIOD,
new TimerEventPerformer() {
public void perform( TimerEvent ev ) {
checkMemoryUsage();
}
}
);
if( DEBUG_PRINT_MEM ) {
Timer printer = new Timer("printer");
printer.addPeriodicEvent(
DEBUG_PRINT_TIME,
new TimerEventPerformer() {
public void perform( TimerEvent ev ) {
System.out.print("DIRECT: given=" +bytesOut/1024/1024+ "MB, returned=" +bytesIn/1024/1024+ "MB, ");
long in_use = bytesOut - bytesIn;
if( in_use < 1024*1024 ) System.out.print( "in use=" +in_use+ "B, " );
else System.out.print( "in use=" +in_use/1024/1024+ "MB, " );
long free = bytesFree();
if( free < 1024*1024 ) System.out.print( "free=" +free+ "B" );
else System.out.print( "free=" +free/1024/1024+ "MB" );
System.out.println();
printInUse( false );
long free_mem = Runtime.getRuntime().freeMemory() /1024/1024;
long max_mem = Runtime.getRuntime().maxMemory() /1024/1024;
long total_mem = Runtime.getRuntime().totalMemory() /1024/1024;
System.out.println("HEAP: max=" +max_mem+ "MB, total=" +total_mem+ "MB, free=" +free_mem+ "MB");
System.out.println();
}
}
);
}
}
/**
* Allocate and return a new direct ByteBuffer.
*/
private ByteBuffer allocateNewBuffer(final int _size) {
try {
return ByteBuffer.allocateDirect(_size);
}
catch (OutOfMemoryError e) {
//Debug.out("Running garbage collector...");
clearBufferPools();
runGarbageCollection();
try {
return ByteBuffer.allocateDirect(_size);
}catch (OutOfMemoryError ex) {
String msg = "Memory allocation failed: Out of direct memory space.\n"
+ "To fix: Use the -XX:MaxDirectMemorySize=512m command line option,\n"
+ "or upgrade your Java JRE to version 1.4.2_05 or 1.5 series or newer.";
Debug.out( msg );
LGLogger.logUnrepeatableAlert( LGLogger.AT_ERROR, msg );
printInUse( true );
throw( ex );
}
}
}
/**
* Retrieve a buffer from the buffer pool of size at least
* <b>length</b>, and no larger than <b>DirectByteBufferPool.MAX_SIZE</b>
*/
public static DirectByteBuffer
getBuffer(
byte _allocator,
int _length)
{
if (_length < 1) {
Debug.out("requested length [" +_length+ "] < 1");
return null;
}
if (_length > MAX_SIZE) {
Debug.out("requested length [" +_length+ "] > MAX_SIZE [" +MAX_SIZE+ "]");
return null;
}
return pool.getBufferHelper(_allocator,_length);
}
/**
* Retrieve an appropriate buffer from the free pool, or
* create a new one if the pool is empty.
*/
private DirectByteBuffer
getBufferHelper(
byte _allocator,
int _length)
{
Integer reqVal = new Integer(_length);
//loop through the buffer pools to find a buffer big enough
Iterator it = buffersMap.keySet().iterator();
while (it.hasNext()) {
Integer keyVal = (Integer)it.next();
//check if the buffers in this pool are big enough
if (reqVal.compareTo(keyVal) <= 0) {
ArrayList bufferPool = (ArrayList)buffersMap.get(keyVal);
ByteBuffer buff;
synchronized ( poolsLock ) {
//make sure we don't remove a buffer when running compaction
//if there are no free buffers in the pool, create a new one.
//otherwise use one from the pool
if (bufferPool.isEmpty()) {
buff = allocateNewBuffer(keyVal.intValue());
}else{
synchronized ( bufferPool ) {
buff = (ByteBuffer)bufferPool.remove(bufferPool.size() - 1);
}
}
}
// clear doesn't actually zero the data, it just sets pos to 0 etc.
buff.clear(); //scrub the buffer
buff.limit( _length );
bytesOut += buff.capacity();
DirectByteBuffer dbb = new DirectByteBuffer( _allocator, buff, this );
if ( DEBUG_PRINT_MEM ){
synchronized( handed_out ){
if ( handed_out.put( buff, dbb ) != null ){
Debug.out( "buffer handed out twice!!!!");
throw( new RuntimeException( "Buffer handed out twice" ));
}
//System.out.println( "[" + handed_out.size() + "] -> " + buff + ", bytesIn = " + bytesIn + ", bytesOut = " + bytesOut );
}
}
// addInUse( dbb.capacity() );
return dbb;
}
}
//we should never get here
Debug.out("Unable to find an appropriate buffer pool");
throw( new RuntimeException( "Unable to find an appropriate buffer pool" ));
}
/**
* Return the given buffer to the appropriate pool.
*/
private void
free(ByteBuffer _buffer)
{
Integer buffSize = new Integer(_buffer.capacity());
ArrayList bufferPool = (ArrayList)buffersMap.get(buffSize);
if (bufferPool != null) {
//no need to sync around 'poolsLock', as adding during compaction is ok
synchronized ( bufferPool ) {
bufferPool.add(_buffer);
}
}
else {
Debug.out("Invalid buffer given; could not find proper buffer pool");
}
}
/**
* Clears the free buffer pools so that currently
* unused buffers can be garbage collected.
*/
private void clearBufferPools() {
Iterator it = buffersMap.values().iterator();
while (it.hasNext()) {
ArrayList bufferPool = (ArrayList)it.next();
bufferPool.clear();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -