@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;
  }