public ByteBuff allocate(int size) {
    if (size < 0) {
      throw new IllegalArgumentException("size to allocate should >=0");
    }
    // If disabled the reservoir, just allocate it from on-heap.
    if (!isReservoirEnabled() || size == 0) {
      return ByteBuff.wrap(allocateOnHeap(size));
    }
    int reminder = size % bufSize;
    int len = size / bufSize + (reminder > 0 ? 1 : 0);
    List<ByteBuffer> bbs = new ArrayList<>(len);
    // Allocate from ByteBufferPool until the remaining is less than minSizeForReservoirUse or
    // reservoir is exhausted.
    int remain = size;
    while (remain >= minSizeForReservoirUse) {
      ByteBuffer bb = this.getBuffer();
      if (bb == null) {
        break;
      }
      bbs.add(bb);
      remain -= bufSize;
    }
    int lenFromReservoir = bbs.size();
    if (remain > 0) {
      // If the last ByteBuffer is too small or the reservoir can not provide more ByteBuffers, we
      // just allocate the ByteBuffer from on-heap.
      bbs.add(allocateOnHeap(remain));
    }

    ByteBuff bb;
    // we only need a recycler if we successfully pulled from the pool
    // this matters for determining whether to add leak detection in RefCnt
    if (lenFromReservoir == 0) {
      bb = ByteBuff.wrap(bbs);
    } else {
      bb = ByteBuff.wrap(bbs, () -> {
        for (int i = 0; i < lenFromReservoir; i++) {
          this.putbackBuffer(bbs.get(i));
        }
      });
    }

    bb.limit(size);
    return bb;
  }
Allocate size bytes from the ByteBufAllocator, Note to call the {@link ByteBuff#release()} if
no need any more, otherwise the memory leak happen in NIO ByteBuffer pool.
@param size to allocate
@return an ByteBuff with the desired size.
hbase-common/src/main/java/org/apache/hadoop/hbase/io/ByteBuffAllocator.java