You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
7.0 KiB
238 lines
7.0 KiB
/*
|
|
* 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=<size>".
|
|
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;
|
|
}
|