parent
dad3160706
commit
0cd2eba0a6
@ -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=<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;
|
||||
}
|
Loading…
Reference in new issue