@VisibleForTesting
  public boolean reserveMemory(final long memoryToReserve,
      boolean waitForEviction, AtomicBoolean isStopped) {
    // TODO: if this cannot evict enough, it will spin infinitely. Terminate at some point?
    int badCallCount = 0;
    long evictedTotalMetric = 0, reservedTotalMetric = 0, remainingToReserve = memoryToReserve;
    boolean result = true;
    while (remainingToReserve > 0) {
      long usedMem = usedMemory.get(), newUsedMem = usedMem + remainingToReserve;
      if (newUsedMem <= maxSize) {
        if (usedMemory.compareAndSet(usedMem, newUsedMem)) {
          reservedTotalMetric += remainingToReserve;
          break;
        }
        continue;
      }
      if (evictor == null) return false;
      // TODO: for one-block case, we could move notification for the last block out of the loop.
      long evicted = evictor.evictSomeBlocks(remainingToReserve);
      if (evicted == 0) {
        ++badCallCount;
        if (!waitForEviction) {
          result = false;
          break;
        }

        if (isStopped != null && isStopped.get()) {
          result = false;
          break;
        }
        try {
          Thread.sleep(badCallCount > 9 ? 1000 : (1 << badCallCount));
        } catch (InterruptedException e) {
          LlapIoImpl.LOG.warn("Thread interrupted"); // We currently don't expect this.
          Thread.currentThread().interrupt();
          result = false;
          break;
        }
        continue;
      }
      evictedTotalMetric += evicted;
      badCallCount = 0;
      // Adjust the memory - we have to account for what we have just evicted.
      while (true) {
        long availableToReserveAfterEvict = maxSize - usedMem + evicted;
        long reservedAfterEvict = Math.min(remainingToReserve, availableToReserveAfterEvict);
        if (usedMemory.compareAndSet(usedMem, usedMem - evicted + reservedAfterEvict)) {
          remainingToReserve -= reservedAfterEvict;
          reservedTotalMetric += reservedAfterEvict;
          break;
        }
        usedMem = usedMemory.get();
      }
    }
    if (!result) {
      releaseMemory(reservedTotalMetric);
      reservedTotalMetric = 0;
    }
    metrics.incrCacheCapacityUsed(reservedTotalMetric - evictedTotalMetric);
    return result;
  }
Implementation of memory manager for low level cache. Note that memory is released during
reserve most of the time, by calling the evictor to evict some memory. releaseMemory is
called rarely.
llap-server/src/java/org/apache/hadoop/hive/llap/cache/LowLevelCacheMemoryManager.java