src/share/classes/java/nio/Bits.java

Print this page

        

*** 24,33 **** --- 24,38 ---- */ package java.nio; import java.security.AccessController; + import java.util.concurrent.atomic.AtomicLong; + import java.util.concurrent.atomic.LongAdder; + + import sun.misc.JavaLangRefAccess; + import sun.misc.SharedSecrets; import sun.misc.Unsafe; import sun.misc.VM; /** * Access to bits, native and otherwise.
*** 619,677 **** // A user-settable upper limit on the maximum amount of allocatable // direct buffer memory. This value may be changed during VM // initialization if it is launched with "-XX:MaxDirectMemorySize=<size>". private static volatile long maxMemory = VM.maxDirectMemory(); ! private static volatile long reservedMemory; ! private static volatile long totalCapacity; ! private static volatile long count; ! private static boolean memoryLimitSet = false; // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size, int cap) { ! synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } ! // -XX:MaxDirectMemorySize limits the total capacity rather than the ! // actual memory usage, which will differ when buffers are page ! // aligned. ! if (cap <= maxMemory - totalCapacity) { ! reservedMemory += size; ! totalCapacity += cap; ! count++; return; } } System.gc(); try { ! Thread.sleep(100); ! } catch (InterruptedException x) { ! // Restore interrupt status ! Thread.currentThread().interrupt(); } ! synchronized (Bits.class) { ! if (totalCapacity + cap > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); ! reservedMemory += size; ! totalCapacity += cap; ! count++; } } ! static synchronized void unreserveMemory(long size, int cap) { ! if (reservedMemory > 0) { ! reservedMemory -= size; ! totalCapacity -= cap; ! count--; ! assert (reservedMemory > -1); } } // -- Monitoring of direct buffer usage -- static { --- 624,730 ---- // A user-settable upper limit on the maximum amount of allocatable // direct buffer memory. This value may be changed during VM // initialization if it is launched with "-XX:MaxDirectMemorySize=<size>". private static volatile long maxMemory = VM.maxDirectMemory(); ! private static final AtomicLong reservedMemory = new AtomicLong(); ! private static final AtomicLong totalCapacity = new AtomicLong(); ! private static final AtomicLong count = new AtomicLong(); ! private static volatile boolean memoryLimitSet = false; ! // max. number of sleeps during try-reserving with exponentially ! // increasing delay before throwing OutOfMemoryError: ! // 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s) ! // which means that OOME will be thrown after 0.5 s of trying ! private static final int MAX_SLEEPS = 9; // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size, int cap) { ! if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } ! ! // optimist! ! if (tryReserveMemory(size, cap)) { ! return; ! } ! ! final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); ! ! // retry while helping enqueue pending Reference objects ! // which includes executing pending Cleaner(s) which includes ! // Cleaner(s) that free direct buffer memory ! while (jlra.tryHandlePendingReference()) { ! if (tryReserveMemory(size, cap)) { return; } } + // trigger VM's Reference processing System.gc(); + + // a retry loop with exponential back-off delays + // (this gives VM some time to do it's job) + boolean interrupted = false; try { ! long sleepTime = 1; ! int sleeps = 0; ! while (true) { ! if (tryReserveMemory(size, cap)) { ! return; ! } ! if (sleeps >= MAX_SLEEPS) { ! break; ! } ! if (!jlra.tryHandlePendingReference()) { ! try { ! Thread.sleep(sleepTime); ! sleepTime <<= 1; ! sleeps++; ! } catch (InterruptedException e) { ! interrupted = true; } ! } ! } ! ! // no luck throw new OutOfMemoryError("Direct buffer memory"); ! ! } finally { ! if (interrupted) { ! // don't swallow interrupts ! Thread.currentThread().interrupt(); ! } ! } } + private static boolean tryReserveMemory(long size, int cap) { + + // -XX:MaxDirectMemorySize limits the total capacity rather than the + // actual memory usage, which will differ when buffers are page + // aligned. + long totalCap; + while (cap <= maxMemory - (totalCap = totalCapacity.get())) { + if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) { + reservedMemory.addAndGet(size); + count.incrementAndGet(); + return true; + } } ! return false; } + + + static void unreserveMemory(long size, int cap) { + long cnt = count.decrementAndGet(); + long reservedMem = reservedMemory.addAndGet(-size); + long totalCap = totalCapacity.addAndGet(-cap); + assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0; } // -- Monitoring of direct buffer usage -- static {
*** 685,703 **** public String getName() { return "direct"; } @Override public long getCount() { ! return Bits.count; } @Override public long getTotalCapacity() { ! return Bits.totalCapacity; } @Override public long getMemoryUsed() { ! return Bits.reservedMemory; } }; } @Override public ByteBuffer newDirectByteBuffer(long addr, int cap, Object ob) { --- 738,756 ---- public String getName() { return "direct"; } @Override public long getCount() { ! return Bits.count.get(); } @Override public long getTotalCapacity() { ! return Bits.totalCapacity.get(); } @Override public long getMemoryUsed() { ! return Bits.reservedMemory.get(); } }; } @Override public ByteBuffer newDirectByteBuffer(long addr, int cap, Object ob) {