@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;
  }
  /**
   * Tries to find and extend a free memory with specified block. If memory can't be extended with
   * such block, then returns false.
   *
   * @param block the memory block to extend free memory with.
   * @return true if memory was extended, otherwise false.
   */
  protected boolean extendFreeMemory(TableBlock block) {
    final int blockAddress = block.getAddress();
    final int blockEnd = block.getEnd();

    TableBlock head = null;
    TableBlock tail = null;
    boolean repeat;
    int loop = 0;
    do {
      // TODO - metrics for these loops
      repeat = false;
      try {
        freeLock.readLock().lock();
        for (TableBlock each : free) {
          if (head == null && each.getAddress() == blockEnd) {
            if (each.lock()) {
              if (each.getAddress() != blockEnd) {
                each.unlock();
              } else {
                head = each;
              }
            } else {
              repeat = true;
            }
          } else if (tail == null && each.getEnd() == blockAddress) {
            if (each.lock()) {
              if (each.getEnd() != blockAddress) {
                each.unlock();
              } else {
                tail = each;
              }
            } else {
              repeat = true;
            }
          }
          if (head != null && tail != null) {
            repeat = false;
            break;
          }
        }
      } finally {
        freeLock.readLock().unlock();
      }
      loop++;
      if (loop > MAX_ALLOC_LOOPS) {
        break;
      } else if (loop == MAX_ALLOC_LOOPS) {
        sleep();
      } else if (loop >= YIELD_MARGIN) {
        Thread.yield();
      }
    } while (repeat);

    // if there is a head or tail - then resize and return true
    if (tail != null) {
      if (head == null) {
        // only tail is found
        tail.setSize(block.getSize() + tail.getSize());
      } else {
        // head is found, so we just resize tail and remove head
        tail.setSize(block.getSize() + tail.getSize() + head.getSize());
        removeBlock(free, head, freeLock);

        head.unlock();

        // we want to decrease fragmentation twice for this case, because we merged 3 blocks into 1
        // so this is decrement #1 and the next is at the end of method
        // TODO - investigate why double decrement is needed?!
        metrics.decrement("vmtable.fragmentation", 2);
      }

      tail.unlock();
    } else if (head != null) {
      head.resize(blockAddress, block.getSize() + head.getSize());
      head.unlock();
    } else {
      // nothing was changed
      return false;
    }

    metrics.decrement("vmtable.fragmentation");
    return true;
  }