diff --git a/44/Bits.java b/44/Bits.java new file mode 100644 index 0000000..5b36c8c --- /dev/null +++ b/44/Bits.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +package java.nio; + +import jdk.internal.access.JavaLangRefAccess; +import jdk.internal.access.SharedSecrets; +import jdk.internal.misc.Unsafe; +import jdk.internal.misc.VM; +import jdk.internal.misc.VM.BufferPool; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Access to bits, native and otherwise. + */ + +class Bits { // package-private + + private Bits() { } + + + // -- Swapping -- + + static short swap(short x) { + return Short.reverseBytes(x); + } + + static char swap(char x) { + return Character.reverseBytes(x); + } + + static int swap(int x) { + return Integer.reverseBytes(x); + } + + static long swap(long x) { + return Long.reverseBytes(x); + } + + + // -- Unsafe access -- + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + // -- Processor and memory-system properties -- + + private static int PAGE_SIZE = -1; + + static int pageSize() { + if (PAGE_SIZE == -1) + PAGE_SIZE = UNSAFE.pageSize(); + return PAGE_SIZE; + } + + static long pageCount(long size) { + return (size + (long)pageSize() - 1L) / pageSize(); + } + + private static boolean UNALIGNED = UNSAFE.unalignedAccess(); + + static boolean unaligned() { + return UNALIGNED; + } + + + // -- Direct memory management -- + + // 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=". + private static volatile long MAX_MEMORY = VM.maxDirectMemory(); + private static final AtomicLong RESERVED_MEMORY = new AtomicLong(); + private static final AtomicLong TOTAL_CAPACITY = new AtomicLong(); + private static final AtomicLong COUNT = new AtomicLong(); + private static volatile boolean MEMORY_LIMIT_SET; + + // 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, long cap) { + + if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) { + MAX_MEMORY = VM.maxDirectMemory(); + MEMORY_LIMIT_SET = true; + } + + // optimist! + if (tryReserveMemory(size, cap)) { + return; + } + + final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); + boolean interrupted = false; + try { + + // Retry allocation until success or there are no more + // references (including Cleaners that might free direct + // buffer memory) to process and allocation still fails. + boolean refprocActive; + do { + try { + refprocActive = jlra.waitForReferenceProcessing(); + } catch (InterruptedException e) { + // Defer interrupts and keep trying. + interrupted = true; + refprocActive = true; + } + if (tryReserveMemory(size, cap)) { + return; + } + } while (refprocActive); + + // trigger VM's Reference processing + System.gc(); + + // A retry loop with exponential back-off delays. + // Sometimes it would suffice to give up once reference + // processing is complete. But if there are many threads + // competing for memory, this gives more opportunities for + // any given thread to make progress. In particular, this + // seems to be enough for a stress test like + // DirectBufferAllocTest to (usually) succeed, while + // without it that test likely fails. Since failure here + // ends in OOME, there's no need to hurry. + long sleepTime = 1; + int sleeps = 0; + while (true) { + if (tryReserveMemory(size, cap)) { + return; + } + if (sleeps >= MAX_SLEEPS) { + break; + } + try { + if (!jlra.waitForReferenceProcessing()) { + Thread.sleep(sleepTime); + sleepTime <<= 1; + sleeps++; + } + } catch (InterruptedException e) { + interrupted = true; + } + } + + // no luck + throw new OutOfMemoryError + ("Cannot reserve " + + size + " bytes of direct buffer memory (allocated: " + + RESERVED_MEMORY.get() + ", limit: " + MAX_MEMORY +")"); + + } finally { + if (interrupted) { + // don't swallow interrupts + Thread.currentThread().interrupt(); + } + } + } + + private static boolean tryReserveMemory(long size, long 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 <= MAX_MEMORY - (totalCap = TOTAL_CAPACITY.get())) { + if (TOTAL_CAPACITY.compareAndSet(totalCap, totalCap + cap)) { + RESERVED_MEMORY.addAndGet(size); + COUNT.incrementAndGet(); + return true; + } + } + + return false; + } + + + static void unreserveMemory(long size, long cap) { + long cnt = COUNT.decrementAndGet(); + long reservedMem = RESERVED_MEMORY.addAndGet(-size); + long totalCap = TOTAL_CAPACITY.addAndGet(-cap); + assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0; + } + + static final BufferPool BUFFER_POOL = new BufferPool() { + @Override + public String getName() { + return "direct"; + } + @Override + public long getCount() { + return Bits.COUNT.get(); + } + @Override + public long getTotalCapacity() { + return Bits.TOTAL_CAPACITY.get(); + } + @Override + public long getMemoryUsed() { + return Bits.RESERVED_MEMORY.get(); + } + }; + + // These numbers represent the point at which we have empirically + // determined that the average cost of a JNI call exceeds the expense + // of an element by element copy. These numbers may change over time. + static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; + static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; +}