@Override
  public Block allocate(int size) {
    if (size <= 0) {
      throw new OutOfBoundException("Size can't be negative or be zero: " + size);
    }

    metrics.increment("vmtable.totalAllocations");

    TimeContext timer = metrics.getTimer("vmtable.allocationTime");
    timer.start();

    final TableBlock freeBlock = findBlockToAllocateFrom(size);
    if (freeBlock != null) {
      final TableBlock result;
      try {
        result = new TableBlock(freeBlock.getAddress(), size);
        if (freeBlock.getSize() == size) {
          freeBlock.resize(0, 0);
          removeBlock(free, freeBlock, freeLock);
          metrics.decrement("vmtable.fragmentation");
        } else {
          freeBlock.resize(freeBlock.getAddress() + size, freeBlock.getSize() - size);
          metrics.increment("vmtable.fragmentation");
        }
        freeMemorySize.addAndGet(-size);
      } finally {
        // unlock asap
        freeBlock.unlock();
      }

      insertBlock(used, result, usedLock);

      usedMemorySize.addAndGet(size);

      timer.stop();
      metrics.mark("vmtable.freeSize", freeMemorySize.longValue());
      metrics.mark("vmtable.usedSize", usedMemorySize.longValue());
      metrics.mark("vmtable.freeBlocksCount", free.size());
      metrics.mark("vmtable.usedBlocksCount", used.size());

      return result;
    }

    timer.stop();

    metrics.increment("vmtable.failedAllocations");

    return null;
  }
  @Override
  public boolean free(Block block) {
    if (block == null) {
      return false;
    }

    metrics.increment("vmtable.totalFrees");

    TimeContext timer = metrics.getTimer("vmtable.freeTime");
    timer.start();

    TableBlock tableBlock = getSimilarBlock(used, block, usedLock);
    if (tableBlock != null) {
      if (removeBlock(used, tableBlock, usedLock)) {
        int size = tableBlock.getSize();
        usedMemorySize.addAndGet(-size);

        addFreeBlock(new TableBlock(tableBlock.getAddress(), tableBlock.getSize()));

        freeMemorySize.addAndGet(size);

        tableBlock.resize(0, 0);
        tableBlock.unlock();

        timer.stop();

        metrics.mark("vmtable.freeSize", freeMemorySize.longValue());
        metrics.mark("vmtable.usedSize", usedMemorySize.longValue());
        metrics.mark("vmtable.freeBlocksCount", free.size());
        metrics.mark("vmtable.usedBlocksCount", used.size());

        return true;
      }
    }

    timer.stop();

    metrics.increment("vmtable.failedFrees");

    return false;
  }
  @Override
  public void increaseSize(int size) {
    final int freeSize = freeMemorySize.get();
    final int usedSize = usedMemorySize.get();
    final int totalSize = freeSize + usedSize;

    // increase memory size
    int incSize = size - totalSize;
    addFreeBlock(new TableBlock(totalSize, incSize));
    freeMemorySize.addAndGet(incSize);
    metrics.increment("vmtable.increases");
  }
  protected TableBlock findBlockToAllocateFrom(int size) {

    // TODO - do we need this loop restriction?
    int loop = 0;
    boolean repeat;
    do {
      repeat = false;
      try {
        freeLock.readLock().lock();
        for (TableBlock each : free) {
          if (each.getSize() >= size) {
            if (each.lock()) {
              if (each.getSize() >= size) {
                return each;
              }
              each.unlock();
            } else {
              // looks like there was a block that was enough to allocate from
              // but now it's locked, so need to loop the list of blocks again.
              repeat = true;
            }
          }
        }
      } finally {
        freeLock.readLock().unlock();
      }
      metrics.increment("vmtable.loopsToFindFitBlock");

      // avoid ever looping, instead prefer to wait or exit the loop
      loop++;
      if (loop > MAX_ALLOC_LOOPS) {
        break;
      } else if (loop == MAX_ALLOC_LOOPS) {
        sleep();
      } else if (loop >= YIELD_MARGIN) {
        Thread.yield();
      }
    } while (repeat);

    return null;
  }
 /**
  * Add a block to the free memory list.
  *
  * @param block the block that need to be added to the free memory list.
  */
 protected void addFreeBlock(TableBlock block) {
   if (!extendFreeMemory(block)) {
     insertBlock(free, block, freeLock);
     metrics.increment("vmtable.fragmentation");
   }
 }