luk*_*keg 4 java memory-management
根据各种 来源(虽然在JavaDoc中没有特别提到),ByteBuffer.allocateDirect从主JVM堆中分配内存.我可以确认使用Java Mission Control,看到调用的程序ByteBuffer n = ByteBuffer.allocateDirect(Integer.MAX_VALUE)没有使用大量的Java Heap内存:
但是,当限制JVM堆内存时,此堆外内存分配将停止工作.例如,当我使用-Xmx1g选项运行JVM时,allocateDirect调用会导致以下异常:Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory.我不完全理解这个JVM选项如何与堆外直接内存分配相关,因为 - 根据文档 - 该-Xmx选项设置Java堆空间大小.如果我使用getUnsafe().allocateMemory(Integer.MAX_VALUE);内存分配内存成功分配.我的JVM如下:
java版"10"2018-03-20 Java(TM)SE运行时环境18.3(版本10 + 46)Java HotSpot(TM)64位服务器VM 18.3(版本10 + 46,混合模式)
就是这种行为之间的Xmx和ByteBuffer.allocateDirect预期?
编辑:JDK 1.7中似乎存在(不可重现的)错误,其行为与上述相同.这是一个错误吗?
Jac*_* G. 11
我不得不寻找寻找原因的寻宝者,但是你走了!
首先,我看了一下ByteBuffer#allocateDirect,发现了以下内容:
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
Run Code Online (Sandbox Code Playgroud)
然后我导航到构造函数DirectByteBuffer并找到以下方法调用:
Bits.reserveMemory(size, cap);
Run Code Online (Sandbox Code Playgroud)
看看这种方法,我们看到:
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("Direct buffer memory");
Run Code Online (Sandbox Code Playgroud)
这似乎是你收到此错误的地方,但现在我们需要弄清楚它为什么会引起.为此,我查看了调用tryReserveMemory并找到了以下内容:
private static boolean tryReserveMemory(long size, int cap) {
long totalCap;
while (cap <= maxMemory - (totalCap = totalCapacity.get())) {
if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) {
reservedMemory.addAndGet(size);
count.incrementAndGet();
return true;
}
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
我对这个maxMemory领域很好奇,并且看着它被宣布的地方:
private static volatile long maxMemory = VM.maxDirectMemory();
Run Code Online (Sandbox Code Playgroud)
现在我不得不看看maxDirectMemory内部VM.java:
public static long maxDirectMemory() {
return directMemory;
}
Run Code Online (Sandbox Code Playgroud)
最后,让我们来看看声明directMemory:
// A user-settable upper limit on the maximum amount of allocatable direct
// buffer memory. This value may be changed during VM initialization if
// "java" is launched with "-XX:MaxDirectMemorySize=<size>".
//
// The initial value of this field is arbitrary; during JRE initialization
// it will be reset to the value specified on the command line, if any,
// otherwise to Runtime.getRuntime().maxMemory().
//
private static long directMemory = 64 * 1024 * 1024;
Run Code Online (Sandbox Code Playgroud)
嘿,看那个!如果不使用手动指定"-XX:MaxDirectMemorySize=<size>",则默认为Runtime.getRuntime().maxMemory(),即您设置的堆大小.
看起来-Xmx1G小于Integer.MAX_VALUE字节,调用tryReserveMemory将永远不会返回true,这导致sleeps >= MAX_SLEEPS,打破while循环,抛出你的OutOfMemoryError.
如果我们看一下Runtime.getRuntime().maxMemory(),那么如果你没有指定最大堆大小,我们就会明白为什么它可以工作:
/**
* Returns the maximum amount of memory that the Java virtual machine
* will attempt to use. If there is no inherent limit then the value
* {@link java.lang.Long#MAX_VALUE} will be returned.
*
* @return the maximum amount of memory that the virtual machine will
* attempt to use, measured in bytes
* @since 1.4
*/
public native long maxMemory();
Run Code Online (Sandbox Code Playgroud)