BucketCache uses BucketAllocator to allocate/free blocks, and uses
BucketCache#ramCache and BucketCache#backingMap in order to
determine if a given element is in the cache. The bucket cache can use on-heap or
off-heap memory ByteBufferIOEngine or in a file FileIOEngine to
store/read the block data.

This method will find the buckets that are minimally occupied
and are not reference counted and will free them completely
without any constraint on the access times of the elements,
and as a process will completely free at most the number of buckets
passed, sometimes it might not due to changing refCounts

writerQueues

A list of writer queues. We have a queue per BucketCache.WriterThread we have running.
In other words, the work adding blocks to the BucketCache is divided up amongst the
running WriterThreads. Its done by taking hash of the cache key modulo queue count.
WriterThread when it runs takes whatever has been recently added and 'drains' the entries
to the BucketCache. It then updates the ramCache and backingMap accordingly.

bucketSizesAboveThresholdCount

freeEntireBuckets

This method will find the buckets that are minimally occupied
and are not reference counted and will free them completely
without any constraint on the access times of the elements,
and as a process will completely free at most the number of buckets
passed, sometimes it might not due to changing refCounts

returnBlock

Called when the scanner using the block decides to return the block once its usage
is over.
This API should be called after the block is used, failing to do so may have adverse effects
by preventing the blocks from being evicted because of which it will prevent new hot blocks
from getting added to the block cache. The implementation of the BlockCache will decide
on what to be done with the block based on the memory type of the block's Cacheable.MemoryType.