|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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 { |
|
|
|
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 |
|
|
|
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) |
|
|
|
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 |
|
|
|
static void reserveMemory(long size, long cap) { |
|
|
|
if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) { |
|
MAX_MEMORY = VM.maxDirectMemory(); |
|
MEMORY_LIMIT_SET = true; |
|
} |
|
|
|
|
|
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 |
|
|
|
boolean refprocActive; |
|
do { |
|
try { |
|
refprocActive = jlra.waitForReferenceProcessing(); |
|
} catch (InterruptedException e) { |
|
|
|
interrupted = true; |
|
refprocActive = true; |
|
} |
|
if (tryReserveMemory(size, cap)) { |
|
return; |
|
} |
|
} while (refprocActive); |
|
|
|
|
|
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 |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
throw new OutOfMemoryError |
|
("Cannot reserve " |
|
+ size + " bytes of direct buffer memory (allocated: " |
|
+ RESERVED_MEMORY.get() + ", limit: " + MAX_MEMORY +")"); |
|
|
|
} finally { |
|
if (interrupted) { |
|
|
|
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 |
|
|
|
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 |
|
|
|
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; |
|
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; |
|
} |