private UpfrontAllocatingPageSource( BufferSource source, long max, int maxChunk, int minChunk, boolean fixed) { Long totalPhysical = PhysicalMemory.totalPhysicalMemory(); Long freePhysical = PhysicalMemory.freePhysicalMemory(); if (totalPhysical != null && max > totalPhysical) { throw new IllegalArgumentException( "Attempting to allocate " + DebuggingUtils.toBase2SuffixedString(max) + "B of memory " + "when the host only contains " + DebuggingUtils.toBase2SuffixedString(totalPhysical) + "B of physical memory"); } if (freePhysical != null && max > freePhysical) { LOGGER.warn( "Attempting to allocate {}B of offheap when there is only {}B of free physical memory - some paging will therefore occur.", DebuggingUtils.toBase2SuffixedString(max), DebuggingUtils.toBase2SuffixedString(freePhysical)); } LOGGER.info("Allocating {}B in chunks", DebuggingUtils.toBase2SuffixedString(max)); for (ByteBuffer buffer : allocateBackingBuffers(source, max, maxChunk, minChunk, fixed)) { sliceAllocators.add(new PowerOfTwoAllocator(buffer.capacity())); victimAllocators.add(new PowerOfTwoAllocator(buffer.capacity())); victims.add(new TreeSet<Page>(REGION_COMPARATOR)); buffers.add(buffer); } }
private static PrintStream createAllocatorLog(long max, int maxChunk, int minChunk) throws IOException { String path = System.getProperty(ALLOCATION_LOG_LOCATION); if (path == null) { return null; } else { File allocatorLogFile = File.createTempFile("allocation", ".csv", new File(path)); PrintStream allocatorLogStream = new PrintStream(allocatorLogFile, "US-ASCII"); allocatorLogStream.printf("Timestamp: %s%n", new Date()); allocatorLogStream.printf("Allocating: %sB%n", DebuggingUtils.toBase2SuffixedString(max)); allocatorLogStream.printf("Max Chunk: %sB%n", DebuggingUtils.toBase2SuffixedString(maxChunk)); allocatorLogStream.printf("Min Chunk: %sB%n", DebuggingUtils.toBase2SuffixedString(minChunk)); return allocatorLogStream; } }
private Page allocateFromFree(int size, boolean victim, OffHeapStorageArea owner) { if (Integer.bitCount(size) != 1) { int rounded = Integer.highestOneBit(size) << 1; LOGGER.info( "Request to allocate {}B will allocate {}B", size, DebuggingUtils.toBase2SuffixedString(rounded)); size = rounded; } if (isUnavailable(size)) { return null; } synchronized (this) { for (int i = 0; i < sliceAllocators.size(); i++) { int address = sliceAllocators.get(i).allocate(size, victim ? CEILING : FLOOR); if (address >= 0) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "Allocating a {}B buffer from chunk {} &{}", new Object[] {DebuggingUtils.toBase2SuffixedString(size), i, address}); } ByteBuffer b = ((ByteBuffer) buffers.get(i).limit(address + size).position(address)).slice(); Page p = new Page(b, i, address, owner); if (victim) { victims.get(i).add(p); } else { victimAllocators.get(i).claim(address, size); } if (!risingThresholds.isEmpty()) { long allocated = getAllocatedSize(); fireThresholds(allocated - size, allocated); } return p; } } markUnavailable(size); return null; } }
@Override public synchronized String toString() { StringBuilder sb = new StringBuilder("UpfrontAllocatingPageSource"); for (int i = 0; i < buffers.size(); i++) { sb.append("\nChunk ").append(i + 1).append('\n'); sb.append("Size : ") .append(DebuggingUtils.toBase2SuffixedString(buffers.get(i).capacity())) .append("B\n"); sb.append("Free Allocator : ").append(sliceAllocators.get(i)).append('\n'); sb.append("Victim Allocator : ").append(victimAllocators.get(i)); } return sb.toString(); }
/** * Frees the supplied buffer. * * <p>If the given buffer was not allocated by this source or has already been freed then an * {@code AssertionError} is thrown. */ @Override public synchronized void free(Page page) { if (page.isFreeable()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "Freeing a {}B buffer from chunk {} &{}", new Object[] { DebuggingUtils.toBase2SuffixedString(page.size()), page.index(), page.address() }); } markAllAvailable(); sliceAllocators.get(page.index()).free(page.address(), page.size()); victims.get(page.index()).remove(page); victimAllocators.get(page.index()).tryFree(page.address(), page.size()); if (!fallingThresholds.isEmpty()) { long allocated = getAllocatedSize(); fireThresholds(allocated + page.size(), allocated); } } }
private static Collection<ByteBuffer> allocateBackingBuffers( BufferSource source, long max, int maxChunk, int minChunk, boolean fixed) { Collection<ByteBuffer> buffers = new LinkedList<ByteBuffer>(); PrintStream allocatorLog; try { allocatorLog = createAllocatorLog(max, maxChunk, minChunk); } catch (IOException e) { LOGGER.warn("Exception creating allocation log", e); allocatorLog = null; } long start = System.nanoTime(); if (allocatorLog != null) { allocatorLog.printf( "timestamp,duration,size,allocated,physfree,totalswap,freeswap,committed%n"); } long progressStep = Math.max(PROGRESS_LOGGING_THRESHOLD, (long) (max * PROGRESS_LOGGING_STEP_SIZE)); long lastFailureAt = 0; long currentChunkSize = maxChunk; long allocated = 0; long nextProgressLogAt = progressStep; while (allocated < max) { long blockStart = System.nanoTime(); ByteBuffer b = source.allocateBuffer((int) Math.min(currentChunkSize, (max - allocated))); long blockDuration = System.nanoTime() - blockStart; if (b == null) { if (fixed || (currentChunkSize >>> 1) < minChunk) { throw new IllegalArgumentException( "An attempt was made to allocate more off-heap memory than the JVM can allow." + " The limit on off-heap memory size is given by the -XX:MaxDirectMemorySize command (or equivalent)."); } else { LOGGER.info( "Allocated {}B in {}B chunks.", new Object[] { DebuggingUtils.toBase2SuffixedString(allocated - lastFailureAt), DebuggingUtils.toBase2SuffixedString(currentChunkSize) }); currentChunkSize >>>= 1; lastFailureAt = allocated; } } else { buffers.add(b); allocated += b.capacity(); if (allocatorLog != null) { allocatorLog.printf( "%d,%d,%d,%d,%d,%d,%d,%d%n", System.nanoTime() - start, blockDuration, b.capacity(), allocated, PhysicalMemory.freePhysicalMemory(), PhysicalMemory.totalSwapSpace(), PhysicalMemory.freeSwapSpace(), PhysicalMemory.ourCommittedVirtualMemory()); } if (allocated > nextProgressLogAt) { LOGGER.info("Allocation {}% complete", (100 * allocated) / max); nextProgressLogAt += progressStep; } } } if (allocatorLog != null) { allocatorLog.close(); } LOGGER.info( "Allocated {}B in {}B chunks.", new Object[] { DebuggingUtils.toBase2SuffixedString(allocated - lastFailureAt), DebuggingUtils.toBase2SuffixedString(currentChunkSize) }); long duration = System.nanoTime() - start; LOGGER.info( "Took {} ms to create off-heap storage of {}B.", TimeUnit.NANOSECONDS.toMillis(duration), DebuggingUtils.toBase2SuffixedString(max)); return Collections.unmodifiableCollection(buffers); }