Class BufferPool


  • public class BufferPool
    extends java.lang.Object
    A pool of ByteBuffers that can be recycled to reduce system direct memory fragmentation and improve buffer allocation performance.

    Each BufferPool instance has one BufferPool.GlobalPool which allocates two kinds of chunks:

    • Macro Chunk
      • A memory slab that has size of MACRO_CHUNK_SIZE which is 64 * NORMAL_CHUNK_SIZE
      • Used to allocate normal chunk with size of NORMAL_CHUNK_SIZE
    • Normal Chunk
      • Used by BufferPool.LocalPool to serve buffer allocation
      • Minimum allocation unit is NORMAL_CHUNK_SIZE / 64
    BufferPool.GlobalPool maintains two kinds of freed chunks, fully freed chunks where all buffers are released, and partially freed chunks where some buffers are not released, eg. held by ChunkCache. Partially freed chunks are used to improve cache utilization and have lower priority compared to fully freed chunks.

    BufferPool.LocalPool is a thread local pool to serve buffer allocation requests. There are two kinds of local pool:

    • Normal Pool:
      • used to serve allocation size that is larger than half of NORMAL_ALLOCATION_UNIT but less than NORMAL_CHUNK_SIZE
      • when there is insufficient space in the local queue, it will request global pool for more normal chunks
      • when normal chunk is recycled either fully or partially, it will be passed to global pool to be used by other pools
    • Tiny Pool:
      • used to serve allocation size that is less than NORMAL_ALLOCATION_UNIT
      • when there is insufficient space in the local queue, it will request parent normal pool for more tiny chunks
      • when tiny chunk is fully freed, it will be passed to paretn normal pool and corresponding buffer in the parent normal chunk is freed
    Note: even though partially freed chunks improves cache utilization when chunk cache holds outstanding buffer for arbitrary period, there is still fragmentation in the partially freed chunk because of non-uniform allocation size.

    The lifecycle of a normal Chunk:

        new                      acquire                      release                    recycle
     ────────→ in GlobalPool ──────────────→ in LocalPool ──────────────→ EVICTED  ──────────────────┐
               owner = null                  owner = LocalPool            owner = null               │
               status = IN_USE               status = IN_USE              status = EVICTED           │
                  ready                      serves get / free            serves free only           │
                    ↑                                                                                │
                    └────────────────────────────────────────────────────────────────────────────────┘
     
    • Constructor Detail

      • BufferPool

        public BufferPool​(java.lang.String name,
                          long memoryUsageThreshold,
                          boolean recyclePartially)
    • Method Detail

      • create

        public BufferPool.LocalPool create()
        Returns:
        a local pool instance and caller is responsible to release the pool
      • get

        public java.nio.ByteBuffer get​(int size,
                                       BufferType bufferType)
      • getAtLeast

        public java.nio.ByteBuffer getAtLeast​(int size,
                                              BufferType bufferType)
      • tryGet

        public java.nio.ByteBuffer tryGet​(int size)
        Unlike the get methods, this will return null if the pool is exhausted
      • tryGetAtLeast

        public java.nio.ByteBuffer tryGetAtLeast​(int size)
      • put

        public void put​(java.nio.ByteBuffer buffer)
      • putUnusedPortion

        public void putUnusedPortion​(java.nio.ByteBuffer buffer)
      • setRecycleWhenFreeForCurrentThread

        public void setRecycleWhenFreeForCurrentThread​(boolean recycleWhenFree)
      • sizeInBytes

        public long sizeInBytes()
        Returns:
        buffer size being allocated, including pooled buffers and unpooled buffers
      • usedSizeInBytes

        public long usedSizeInBytes()
        Returns:
        buffer size being used, including used pooled buffers and unpooled buffers
      • overflowMemoryInBytes

        public long overflowMemoryInBytes()
        Returns:
        unpooled buffer size being allocated outside of buffer pool.
      • memoryUsageThreshold

        public long memoryUsageThreshold()
        Returns:
        maximum pooled buffer size in bytes
      • globalPool

        public org.apache.cassandra.utils.memory.BufferPool.GlobalPool globalPool()
      • releaseLocal

        public void releaseLocal()
        Forces to recycle free local chunks back to the global pool. This is needed because if buffers were freed by a different thread than the one that allocated them, recycling might not have happened and the local pool may still own some fully empty chunks.
      • debug

        public void debug​(org.apache.cassandra.utils.memory.BufferPool.Debug newDebug,
                          BufferPool.DebugLeaks newDebugLeaks)
      • roundUp

        public static int roundUp​(int size)
      • roundUp

        public static int roundUp​(int size,
                                  int unit)
      • shutdownLocalCleaner

        public void shutdownLocalCleaner​(long timeout,
                                         java.util.concurrent.TimeUnit unit)
                                  throws java.lang.InterruptedException,
                                         java.util.concurrent.TimeoutException
        Throws:
        java.lang.InterruptedException
        java.util.concurrent.TimeoutException
      • unsafeReset

        public void unsafeReset()
        This is not thread safe and should only be used for unit testing.