private void freeEntireBuckets(int completelyFreeBucketsNeeded) {
    if (!isCacheInitialized("BucketCache::freeEntireBuckets")) {
      return;
    }

    if (completelyFreeBucketsNeeded != 0) {
      // First we will build a set where the offsets are reference counted, usually
      // this set is small around O(Handler Count) unless something else is wrong
      Set<Integer> inUseBuckets = new HashSet<>();
      backingMap.forEach((k, be) -> {
        if (be.isRpcRef()) {
          inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));
        }
      });
      Set<Integer> candidateBuckets =
        bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);
      for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {
        if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {
          evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());
        }
      }
    }
  }
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
@param completelyFreeBucketsNeeded number of buckets to free
hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java