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