Package org.gudy.azureus2.core3.util

Source Code of org.gudy.azureus2.core3.util.DirectByteBufferPoolReal$sliceDBB

/*
* Created on Nov 4, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc.  All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/


package org.gudy.azureus2.core3.util;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.disk.DiskManager;
import org.gudy.azureus2.core3.logging.LogAlert;
import org.gudy.azureus2.core3.logging.Logger;

import com.aelitis.azureus.core.diskmanager.cache.CacheFileManager;
import com.aelitis.azureus.core.diskmanager.cache.CacheFileManagerFactory;
import com.aelitis.azureus.core.diskmanager.cache.CacheFileManagerStats;

public class
DirectByteBufferPoolReal
  extends DirectByteBufferPool
{
  private static final boolean disable_gc = System.getProperty( "az.disable.explicit.gc", "0" ).equals( "1" );
 
  static{
 
    if ( disable_gc ){
 
      System.out.println( "Explicit GC disabled" );
    }
  }
 
  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    = 120 * 1000;
 
  protected static final boolean        DEBUG_HANDOUT_SIZES    = false;
  protected static final boolean        DEBUG_FREE_SIZES    = false;
 
 
 
 
 
    // 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 = { DiskManager.BLOCK_SIZE + 128 };
 
 
  public static final int MAX_SIZE = BigInteger.valueOf(2).pow(END_POWER).intValue();
 
  private static final DirectByteBufferPoolReal pool = new DirectByteBufferPoolReal();
 

  private final Map buffersMap = new LinkedHashMap(END_POWER - START_POWER + 1);
 
  private final Object poolsLock = new Object();

  private static final int  SLICE_END_SIZE        = 2048
  private static final int    SLICE_ALLOC_CHUNK_SIZE    = 4096;
 

  private static final short[]    SLICE_ENTRY_SIZES    = { 8, 16, 32, 64, 128, 256, 512, 1024, SLICE_END_SIZE };
  private static final short[]    SLICE_ALLOC_MAXS    = { 256, 256, 128, 64, 64646464,   64 };
 
  private static final short[]    SLICE_ENTRY_ALLOC_SIZES = new short[SLICE_ENTRY_SIZES.length];
  private static final List[]      slice_entries       = new List[SLICE_ENTRY_SIZES.length];
  private static final boolean[][]  slice_allocs       = new boolean[SLICE_ENTRY_SIZES.length][];
  private static final boolean[]    slice_alloc_fails    = new boolean[SLICE_ENTRY_SIZES.length];
 
  static{
   
    int mult = COConfigurationManager.getIntParameter( "memory.slice.limit.multiplier" );
   
    if ( mult > 1 ){
     
      for (int i=0;i<SLICE_ALLOC_MAXS.length;i++){
       
        SLICE_ALLOC_MAXS[i] *= mult;
      }
    }
   
    for (int i=0;i<SLICE_ENTRY_SIZES.length;i++){
     
      SLICE_ENTRY_ALLOC_SIZES[i] = (short)(SLICE_ALLOC_CHUNK_SIZE/SLICE_ENTRY_SIZES[i]);
         
      slice_allocs[i] = new boolean[SLICE_ALLOC_MAXS[i]];
     
      slice_entries[i] = new LinkedList();
    }
  }
 
  private static final long[]      slice_use_count   = new long[SLICE_ENTRY_SIZES.length];
 
  private final Map handed_out  = new IdentityHashMap()// for debugging (ByteBuffer has .equals defined on contents, hence IdentityHashMap)
 
  private final Map  size_counts  = new TreeMap();
  private static final long COMPACTION_CHECK_PERIOD = 2*60*1000; //2 min
  private static final long MAX_FREE_BYTES = 10*1024*1024; //10 MB
  private static final long MIN_FREE_BYTES = 1*1024*1024; // 1 MB
 
  private long bytesIn = 0;
  private long bytesOut = 0;
 
 
  protected
  DirectByteBufferPoolReal()
  {
      //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
      SimpleTimer.addPeriodicEvent(
        "DirectBB:compact",
          COMPACTION_CHECK_PERIOD,
          new TimerEventPerformer() {
            public void perform( TimerEvent ev ) {
        
              compactBuffers();
            }
          }
       );
     
      if( DEBUG_PRINT_MEM ) {
        Timer printer = new Timer("printer");
        printer.addPeriodicEvent(
            DEBUG_PRINT_TIME,
            new TimerEventPerformer() {
              public void perform( TimerEvent ev ) {
                printInUse( false );
              }
            }
        );
      }
  }

 
  /**
   * 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 );
         
          Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.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>
   */
  protected DirectByteBuffer
  getBufferSupport(
    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)
  {
    DirectByteBuffer  res;
   
    if ( _length <= SLICE_END_SIZE ){

      res = getSliceBuffer( _allocator, _length );
     
    }else{
       
      ByteBuffer  buff = null;
     
      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);
         
          while( true ){
           
            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());
               
                if ( buff == null ){
                 
                  Debug.out( "allocateNewBuffer for " + _length + " returned null" );
                }
               
                break;
               
              }else{
               
                synchronized ( bufferPool ) {
                 
                  buff = (ByteBuffer)bufferPool.remove(bufferPool.size() - 1);
                }
               
                if ( buff == null ){
                 
                  Debug.out( "buffer pool for " + _length + " contained null entry" );
                 
                }else{
                 
                  break;
                }
              }
            }
          }
         
          break;
        }
      }
   
      if ( buff == null ){
         
        String str = "Unable to find an appropriate buffer pool for " + _length;
       
          Debug.out( str );
         
          throw( new RuntimeException( str ));
      }
     
      res = new DirectByteBuffer( _allocator, buff, this );      
    }
     
          // clear doesn't actually zero the data, it just sets pos to 0 etc.
       
    ByteBuffer buff = res.getBufferInternal();
   
        buff.clear();   //scrub the buffer
       
    buff.limit( _length );
   
        bytesOut += buff.capacity();
                               
        if ( DEBUG_PRINT_MEM || DEBUG_TRACK_HANDEDOUT ){
         
          synchronized( handed_out ){
                   
        if ( DEBUG_HANDOUT_SIZES ){
         
          int  trim_size;
         
          if ( _length < 32 ){
           
            trim_size = 4;
          }else{
           
            trim_size = 16;
          }
         
          int  trim = ((_length+trim_size-1)/trim_size)*trim_size;
         
          Long count = (Long)size_counts.get(new Integer(trim));
         
          if ( count == null ){
           
            size_counts.put( new Integer( trim ), new Long(1));
           
          }else{
           
            size_counts.put( new Integer( trim), new Long( count.longValue() + 1 ));
          }
        }
       
            if ( handed_out.put( buff, res ) != 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( res );
    }
 
 
    /**
     * Return the given buffer to the appropriate pool.
     */
 
  protected void
  returnBufferSupport(
    DirectByteBuffer ddb )
  {   
    ByteBuffer  buff = ddb.getBufferInternal();
   
    if ( buff == null ){
     
      Debug.out( "Returned dbb has null delegate" );
    }
   
    int  capacity = buff.capacity();

    bytesIn += capacity;
       
      if ( DEBUG_TRACK_HANDEDOUT ){
       
        synchronized( handed_out ){

          if ( handed_out.remove( buff ) == null ){
           
            Debug.out( "buffer not handed out" );
           
            throw( new RuntimeException( "Buffer not handed out" ));
          }
         
             // System.out.println( "[" + handed_out.size() + "] <- " + buffer + ", bytesIn = " + bytesIn + ", bytesOut = " + bytesOut );
        }
      }
     
      // remInUse( buffer.capacity() );
   
    if ( capacity <= SLICE_END_SIZE ){
     
      freeSliceBuffer( ddb );
     
    }else{
        Integer buffSize = new Integer(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(buff);
          }
        }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();
    }
  }
 
 
  /**
   * Force system garbage collection.
   */
  private void runGarbageCollection() {
  if ( !disable_gc ){
      if( DEBUG_PRINT_MEM ) {
        System.out.println( "runGarbageCollection()" );
      }
      System.runFinalization();
      System.gc();
  }
  }
 
 
  /**
   * Checks memory usage of free buffers in buffer pools,
   * and calls the compaction method if necessary.
   */
  private void compactBuffers() {
   
    nonsliecd: synchronized (poolsLock)
    {
      long freeSize = bytesFree();
     
      if (freeSize < MIN_FREE_BYTES)
        break nonsliecd;
     
      // apply cleanup pressure based on filling degree
      float remainingFactor;
      if (freeSize > MAX_FREE_BYTES) // downsize to 50% of the limit (not the current capacity!) if we're overlimit
        remainingFactor = 0.5f * MAX_FREE_BYTES / (float) freeSize;
      else // reduce to something between 50% (full: maximum reduction) and 100% (empty: no reduction) 
        remainingFactor = 1.0f - 0.5f * freeSize / (float) MAX_FREE_BYTES;
     
      if (DEBUG_PRINT_MEM)
        System.out.println("Performing cleanup, reducing to " + remainingFactor * 100 + "%");
     
      ArrayList pools = new ArrayList(buffersMap.values());
      for (int i = pools.size() - 1; i >= 0; i--)
      {
        ArrayList pool = (ArrayList) pools.get(i);
        int limit = (int) (pool.size() * remainingFactor); // floor(), this way we can reach 0 at some point
        for (int j = pool.size() - 1; j >= limit; j--)
          pool.remove(j);
      }
     
      runGarbageCollection();
     
      if (DEBUG_PRINT_MEM)
      {
        printInUse(false);
        System.out.println("Cleanup done\n");
      }
    }
 
    compactSlices();
  }
 
 
  private long bytesFree() {
    long bytesUsed = 0;
    synchronized( poolsLock ) {
      //count up total bytes used by free buffers
      Iterator it = buffersMap.keySet().iterator();
      while (it.hasNext()) {
        Integer keyVal = (Integer)it.next();
        ArrayList bufferPool = (ArrayList)buffersMap.get(keyVal);
     
        bytesUsed += keyVal.intValue() * bufferPool.size();
      }
    }
    return bytesUsed;
  }
 
 
  /*
  private final HashMap in_use_counts = new HashMap();
 
  private void addInUse( int size ) {
    Integer key = new Integer( size );
    synchronized( in_use_counts ) {
      Integer count = (Integer)in_use_counts.get( key );
      if( count == null )  count = new Integer( 1 );
      else  count = new Integer( count.intValue() + 1 );
      in_use_counts.put( key, count );
    }
  }
 
  private void remInUse( int size ) {
    Integer key = new Integer( size );
    synchronized( in_use_counts ) {
      Integer count = (Integer)in_use_counts.get( key );
      if( count == null ) System.out.println("count = null");
      if( count.intValue() == 0 ) System.out.println("count = 0");
      in_use_counts.put( key, new Integer( count.intValue() - 1 ) );
    }
  }
 
  private void printInUse() {
    synchronized( in_use_counts ) {
      for( Iterator i = in_use_counts.keySet().iterator(); i.hasNext(); ) {
        Integer key = (Integer)i.next();
        int count = ((Integer)in_use_counts.get( key )).intValue();
        int size = key.intValue();
        if( count > 0 ) {
          if( size < 1024 )  System.out.print("[" +size+ " x " +count+ "] ");
          else  System.out.print("[" +size/1024+ "K x " +count+ "] ");
        }
      }
      System.out.println();
    }
  }
  */
 
    private void
  printInUse(
    boolean    verbose )
    {
      if ( DEBUG_PRINT_MEM ){
       
            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();
       
        CacheFileManager cm  = null;
       
      try{
         cm = CacheFileManagerFactory.getSingleton();
        
      }catch( Throwable e ){
         
        Debug.printStackTrace( e );
      }
  
        synchronized( handed_out ){
       
          Iterator  it = handed_out.values().iterator();
         
          Map  cap_map    = new TreeMap();
          Map  alloc_map  = new TreeMap();
         
          while( it.hasNext()){
           
            DirectByteBuffer  db = (DirectByteBuffer)it.next();
           
            if ( verbose ){
              String  trace = db.getTraceString();
             
              if ( trace != null ){
               
                System.out.println( trace );
              }
            }
           
            Integer cap   = new Integer( db.getBufferInternal().capacity());
            Byte  alloc   = new Byte( db.getAllocator());
           
            myInteger  c = (myInteger)cap_map.get(cap);
           
            if ( c == null ){
             
              c  = new myInteger();
             
              cap_map.put( cap, c );
            }
           
            c.value++;
           
          myInteger  a = (myInteger)alloc_map.get(alloc);
           
            if ( a == null ){
             
              a  = new myInteger();
             
              alloc_map.put( alloc, a );
            }
           
            a.value++;       
          }
         
          it = cap_map.keySet().iterator();
         
          while( it.hasNext()){
           
            Integer    key   = (Integer)it.next();
            myInteger  count   = (myInteger)cap_map.get( key );
           
                if( key.intValue() < 1024 ){
                 
                  System.out.print("[" +key.intValue()+ " x " +count.value+ "] ");
                 
                }else
                 
                  System.out.print("[" +key.intValue()/1024+ "K x " +count.value+ "] ");
                }
          }
         
          System.out.println();
         
        it = alloc_map.keySet().iterator();
         
          while( it.hasNext()){
           
            Byte    key   = (Byte)it.next();
            myInteger  count   = (myInteger)alloc_map.get( key );
           
                System.out.print("[" + DirectByteBuffer.AL_DESCS[key.intValue()]+ " x " +count.value+ "] ");
          }
         
          if ( cm != null ){
           
            CacheFileManagerStats stats = cm.getStats();
           
            System.out.print( " - Cache: " );
             
           
          System.out.print( "sz=" + stats.getSize());
          System.out.print( ",us=" + stats.getUsedSize());
          System.out.print( ",cw=" + stats.getBytesWrittenToCache());
          System.out.print( ",cr=" + stats.getBytesReadFromCache());
          System.out.print( ",fw=" + stats.getBytesWrittenToFile());
          System.out.print( ",fr=" + stats.getBytesReadFromFile());
         
          }
         
          System.out.println();
       
        if ( DEBUG_HANDOUT_SIZES ){
          it = size_counts.entrySet().iterator();
         
          String  str = "";
           
          while( it.hasNext()){
           
            Map.Entry  entry = (Map.Entry)it.next();
           
            str += (str.length()==0?"":",") + entry.getKey() + "=" + entry.getValue();
          }
         
          System.out.println( str );
        }
       
        String str = "";
       
        for (int i=0;i<slice_entries.length;i++){
       
          boolean[]  allocs = slice_allocs[i];
          int  alloc_count = 0;
          for (int j=0;j<allocs.length;j++){
            if( allocs[j]){
              alloc_count++;
            }
          }
          str += (i==0?"":",") + "["+SLICE_ENTRY_SIZES[i]+"]f=" +slice_entries[i].size()+",a=" + (alloc_count*SLICE_ENTRY_ALLOC_SIZES[i]) + ",u=" +slice_use_count[i];
        }
       
        System.out.println( "slices: " + str );

        }
       
        if(DEBUG_FREE_SIZES)
        {
          System.out.print("free block sizes: ");
         
          synchronized (poolsLock)
        {
          Iterator it = buffersMap.keySet().iterator();
          while (it.hasNext())
          {
            Integer keyVal = (Integer) it.next();
            ArrayList bufferPool = (ArrayList) buffersMap.get(keyVal);
           
            int blocksize = keyVal.intValue();
            int blockfootprint = keyVal.intValue() * bufferPool.size();
            if(blockfootprint == 0)
              continue;
            String blocksuffix = "";
            if(blocksize > 1024) { blocksize /= 1024; blocksuffix = "k";}
            if(blocksize > 1024) { blocksize /= 1024; blocksuffix = "M";}
            String footsuffix = "";
            if(blockfootprint > 1024) { blockfootprint /= 1024; footsuffix = "k";}
            if(blockfootprint > 1024) { blockfootprint /= 1024; footsuffix = "M";}
           
           
            System.out.print("["+ blocksize + blocksuffix + ":" + blockfootprint + footsuffix + "] ");
          }
        }
         
          System.out.println();
        }
       
            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();
      }
    }
   
 
    // Slice buffer management
 
    private DirectByteBuffer
  getSliceBuffer(
    byte    _allocator,
    int      _length )
  {
    int  slice_index = getSliceIndex( _length );
   
    List    my_slice_entries   = slice_entries[slice_index];

    synchronized( my_slice_entries ){
 
      boolean[]  my_allocs      = slice_allocs[slice_index];
     
      sliceBuffer  sb = null;
     
      if ( my_slice_entries.size() > 0 ){
       
        sb = (sliceBuffer)my_slice_entries.remove(0);
       
        slice_use_count[slice_index]++;
       
      }else{
       
          // find a free slot
       
        short  slot = -1;
       
        for (short i=0;i<my_allocs.length;i++){
         
          if( !my_allocs[i]){
           
            slot  = i;
     
            break;
          }
        }
       
        if ( slot != -1 ){
         
          short  slice_entry_size   = SLICE_ENTRY_SIZES[slice_index];
          short  slice_entry_count  = SLICE_ENTRY_ALLOC_SIZES[slice_index];
         
          ByteBuffer  chunk = ByteBuffer.allocateDirectslice_entry_size*slice_entry_count  );
         
          my_allocs[slot] = true;
         
          for (short i=0;i<slice_entry_count;i++){
           
            chunk.limit((i+1)*slice_entry_size);
            chunk.position(i*slice_entry_size);
           
            ByteBuffer  slice = chunk.slice();
           
            sliceBuffer new_buffer = new sliceBuffer( slice, slot, i );
           
            if ( i == 0 ){
             
              sb = new_buffer;
             
              slice_use_count[slice_index]++;
             
            }else{
             
              my_slice_entries.add( new_buffer );
            }
          }
        }else{
         
          if ( !slice_alloc_fails[slice_index] ){
           
            slice_alloc_fails[slice_index= true;
           
            Debug.out( "Run out of slice space for '" + SLICE_ENTRY_SIZES[slice_index] + ", reverting to normal allocation" );
          }
         
          ByteBuffer buff = ByteBuffer.allocate( _length );
         
            return( new DirectByteBuffer( _allocator, buff, this ));

        }
      }
     
      sliceDBB dbb = new sliceDBB( _allocator, sb );

      return( dbb );
    }
  }
 
    private void
  freeSliceBuffer(
    DirectByteBuffer  ddb )
  {   
    if ( ddb instanceof sliceDBB ){
     
      int  slice_index = getSliceIndex( ddb.getBufferInternal().capacity());

      List    my_slice_entries   = slice_entries[slice_index];

      synchronized( my_slice_entries ){
     
        my_slice_entries.add( 0, ((sliceDBB)ddb).getSliceBuffer());
      }
    }
  }
 
    private void
  compactSlices()
  {
      // we don't maintain the buffers in sorted order as this is too costly. however, we
      // always allocate and free from the start of the free list, so unused buffer space
      // will be at the end of the list. we periodically sort this list into allocate block
      // order so that if an entire block isn't used for one compaction cycle all of
      // its elements will end up together, if you see what I mean :P
   
      // when we find an entire block is unused then we just drop them from the list to
      // permit them (and the underlying block) to be garbage collected
   
    for (int i=0;i<slice_entries.length;i++){
     
      int      entries_per_alloc   = SLICE_ENTRY_ALLOC_SIZES[i];
 
      List  l = slice_entries[i];
 
        // no point in trying gc if not enough entries
     
      if ( l.size() >= entries_per_alloc ){
     
        synchronized( l ){
         
          Collections.sort( l,
            new Comparator()
            {
              public int
              compare(
                Object  o1,
                Object  o2 )
              {
                sliceBuffer  sb1 = (sliceBuffer)o1;
                sliceBuffer  sb2 = (sliceBuffer)o2;
               
                int  res = sb1.getAllocID() - sb2.getAllocID();
               
                if ( res == 0 ){
                 
                  res = sb1.getSliceID() - sb2.getSliceID();
                }
               
                return( res );
              }
            });
     
          boolean[]  allocs        = slice_allocs[i];
     
          Iterator  it = l.iterator();
         
          int  current_alloc   = -1;
          int entry_count    = 0;
     
          boolean  freed_one  = false;
         
          while( it.hasNext()){
           
            sliceBuffer  sb = (sliceBuffer)it.next();
           
            int  aid = sb.getAllocID();
           
            if ( aid != current_alloc ){
             
              if ( entry_count == entries_per_alloc ){
               
                // System.out.println( "CompactSlices[" + SLICE_ENTRY_SIZES[i]+"] freeing " + aid );
               
                freed_one  = true;
               
                allocs[aid= false;         
              }
             
              current_alloc  = aid;
             
              entry_count    = 1;
             
            }else{
             
              entry_count++;
            }
          }
         
          if ( entry_count == entries_per_alloc ){
           
            // System.out.println( "CompactSlices[" + SLICE_ENTRY_SIZES[i]+"] freeing " + current_alloc );
           
            freed_one  = true;
           
            allocs[current_alloc= false;         
          }
         
          if ( freed_one ){
           
            it = l.iterator();
           
            while( it.hasNext()){
             
              sliceBuffer  sb = (sliceBuffer)it.next();
             
              if ( !allocs[ sb.getAllocID()]){
               
                it.remove();
              }
            }
          }
        }
      }
    }
  }
 
    private int
  getSliceIndex(
    int  _length )
  {
    for (int i=0;i<SLICE_ENTRY_SIZES.length;i++){
     
      if ( _length <= SLICE_ENTRY_SIZES[i] ){
       
        return( i );
      }
    }
   
    Debug.out( "eh?");
   
    return( 0 );
  }
 
    private static class
  sliceBuffer
  {
    private ByteBuffer  buffer;
    private short    alloc_id;
    private short    slice_id;
   
    protected
    sliceBuffer(
      ByteBuffer  _buffer,
      short    _alloc_id,
      short    _slice_id )
    {
      buffer    = _buffer;
      alloc_id  = _alloc_id;
      slice_id  = _slice_id;
    }
   
    protected ByteBuffer
    getBuffer()
    {
      return( buffer );
    }
   
    protected short
    getAllocID()
    {
      return( alloc_id );
    }
   
    protected short
    getSliceID()
    {
      return( slice_id );
    }
  }
 
    private static class
  sliceDBB
    extends DirectByteBuffer
  {
    private sliceBuffer  slice_buffer;

    protected
    sliceDBB(
      byte    _allocator,
      sliceBuffer  _sb )
    { 
      super( _allocator, _sb.getBuffer(), pool );
 
      slice_buffer  = _sb;
    }
   
    protected sliceBuffer
    getSliceBuffer()
    {
      return( slice_buffer );
    }
  }
 
    private static class
  myInteger
    {
      int  value;
    }
}
TOP

Related Classes of org.gudy.azureus2.core3.util.DirectByteBufferPoolReal$sliceDBB

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
script>