⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 directbytebufferpool.java

📁 Azureus is a powerful, full-featured, cross-platform java BitTorrent client
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -