public MapEntry acquire(int length) throws IOException {
		// length check
		int fIndex = mapLengthToFreeEntryArrayIndex(length);
		if (fIndex < 0 || fIndex >= FREE_ENTRY_ARRAY_SIZE) throw new IllegalArgumentException(length + " <= 0 or > max allowed data slot length " + MAX_DATA_SLOT_LENGTH);
		
		// metrics
		this.totalRealUsedSlotSize.addAndGet(length);
		this.totalAcquireCounter.incrementAndGet();
		
		// find exact match
		MapEntry freeEntry = findFreeEntryByLength(fIndex, length);

		if (freeEntry != null) {
			this.totalExactMatchReuseCounter.incrementAndGet();
			freeEntry.MarkInUse();
			freeEntry.putCreatedTime(System.currentTimeMillis());
			return freeEntry;
		}
		
		// find within length + 1 -> 2 * len (so we will waste at most half free space)
		if (fIndex < FREE_ENTRY_ARRAY_SIZE - 1) {
			int fromIndex = fIndex + 1;
			int dIndex = fIndex == 0 ? 1 : fIndex * 2;
			int toIndex = dIndex < FREE_ENTRY_ARRAY_SIZE - 1 ? dIndex : FREE_ENTRY_ARRAY_SIZE - 1;
			SortedSet<Integer> freeEntryIndexCandidates = freeEntryIndexSet.subSet(fromIndex, true, toIndex, true);
			for(int freeIndex : freeEntryIndexCandidates) {
				freeEntry = findFreeEntryByLength(freeIndex, length);
				if (freeEntry != null) {
					this.totalApproximateMatchReuseCounter.incrementAndGet();
					freeEntry.MarkInUse();
					freeEntry.putCreatedTime(System.currentTimeMillis());
					return freeEntry;
				}
			}
		}
		
		// acquire new entry
		freeEntry = this.acquireNew(length);
		freeEntry.MarkInUse();
		freeEntry.putCreatedTime(System.currentTimeMillis());
		return freeEntry;
	}