public V get(int size) {
    ensurePoolSizeInvariant();

    int bucketedSize = getBucketedSize(size);
    int sizeInBytes = -1;

    synchronized (this) {
      Bucket<V> bucket = getBucket(bucketedSize);

      if (bucket != null) {
        // find an existing value that we can reuse
        V value = getValue(bucket);
        if (value != null) {
          Preconditions.checkState(mInUseValues.add(value));

          // It is possible that we got a 'larger' value than we asked for.
          // lets recompute size in bytes here
          bucketedSize = getBucketedSizeForValue(value);
          sizeInBytes = getSizeInBytes(bucketedSize);
          mUsed.increment(sizeInBytes);
          mFree.decrement(sizeInBytes);
          mPoolStatsTracker.onValueReuse(sizeInBytes);
          logStats();
          if (FLog.isLoggable(FLog.VERBOSE)) {
            FLog.v(
                TAG,
                "get (reuse) (object, size) = (%x, %s)",
                System.identityHashCode(value),
                bucketedSize);
          }
          return value;
        }
        // fall through
      }
      // check to see if we can allocate a value of the given size without exceeding the hard cap
      sizeInBytes = getSizeInBytes(bucketedSize);
      if (!canAllocate(sizeInBytes)) {
        throw new PoolSizeViolationException(
            mPoolParams.maxSizeHardCap,
            mUsed.mNumBytes,
            mFree.mNumBytes,
            sizeInBytes);
      }

      // Optimistically assume that allocation succeeds - if it fails, we need to undo those changes
      mUsed.increment(sizeInBytes);
      if (bucket != null) {
        bucket.incrementInUseCount();
      }
    }

    V value = null;
    try {
      // allocate the value outside the synchronized block, because it can be pretty expensive
      // we could have done the allocation inside the synchronized block,
      // but that would have blocked out other operations on the pool
      value = alloc(bucketedSize);
    } catch (Throwable e) {
      // Assumption we made previously is not valid - allocation failed. We need to fix internal
      // counters.
      synchronized (this) {
        mUsed.decrement(sizeInBytes);
        Bucket<V> bucket = getBucket(bucketedSize);
        if (bucket != null) {
          bucket.decrementInUseCount();
        }
      }
      Throwables.propagateIfPossible(e);
    }

    // NOTE: We checked for hard caps earlier, and then did the alloc above. Now we need to
    // update state - but it is possible that a concurrent thread did a similar operation - with
    // the result being that we're now over the hard cap.
    // We are willing to live with that situation - especially since the trim call below should
    // be able to trim back memory usage.
    synchronized(this) {
      Preconditions.checkState(mInUseValues.add(value));
      // If we're over the pool's max size, try to trim the pool appropriately
      trimToSoftCap();
      mPoolStatsTracker.onAlloc(sizeInBytes);
      logStats();
      if (FLog.isLoggable(FLog.VERBOSE)) {
        FLog.v(
            TAG,
            "get (alloc) (object, size) = (%x, %s)",
            System.identityHashCode(value),
            bucketedSize);
      }
    }

    return value;
  }
public V get ( int size ) { ensurePoolSizeInvariant ( ) ; int bucketedSize = getBucketedSize ( size ) ; int sizeInBytes = - 1 ; synchronized ( this ) { Bucket < V > bucket = getBucket ( bucketedSize ) ; if ( bucket != null ) { V value = getValue ( bucket ) ; if ( value != null ) { Preconditions . checkState ( mInUseValues . add ( value ) ) ; bucketedSize = getBucketedSizeForValue ( value ) ; sizeInBytes = getSizeInBytes ( bucketedSize ) ; mUsed . increment ( sizeInBytes ) ; mFree . decrement ( sizeInBytes ) ; mPoolStatsTracker . onValueReuse ( sizeInBytes ) ; logStats ( ) ; if ( FLog . isLoggable ( FLog . VERBOSE ) ) { FLog . v ( TAG , "get (reuse) (object, size) = (%x, %s)" , System . identityHashCode ( value ) , bucketedSize ) ; } return value ; } } sizeInBytes = getSizeInBytes ( bucketedSize ) ; if ( ! canAllocate ( sizeInBytes ) ) { throw new PoolSizeViolationException ( mPoolParams . maxSizeHardCap , mUsed . mNumBytes , mFree . mNumBytes , sizeInBytes ) ; } mUsed . increment ( sizeInBytes ) ; if ( bucket != null ) { bucket . incrementInUseCount ( ) ; } } V value = null ; try { value = alloc ( bucketedSize ) ; } catch ( Throwable e ) { synchronized ( this ) { mUsed . decrement ( sizeInBytes ) ; Bucket < V > bucket = getBucket ( bucketedSize ) ; if ( bucket != null ) { bucket . decrementInUseCount ( ) ; } } Throwables . propagateIfPossible ( e ) ; } synchronized ( this ) { Preconditions . checkState ( mInUseValues . add ( value ) ) ; trimToSoftCap ( ) ; mPoolStatsTracker . onAlloc ( sizeInBytes ) ; logStats ( ) ; if ( FLog . isLoggable ( FLog . VERBOSE ) ) { FLog . v ( TAG , "get (alloc) (object, size) = (%x, %s)" , System . identityHashCode ( value ) , bucketedSize ) ; } } return value ; }
Gets a new 'value' from the pool, if available. Allocates a new value if necessary.
If we need to perform an allocation,
- If the pool size exceeds the max-size soft cap, then we attempt to trim the free portion
of the pool.
- If the pool size exceeds the max-size hard-cap (after trimming), then we throw an
{@link PoolSizeViolationException}
Bucket length constraints are not considered in this function
@param size the logical size to allocate
@return a new value
@throws InvalidSizeException