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