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