我不得不进行一次寻宝游戏来找到原因,但是现在已经找到了!
首先,我查看了ByteBuffer#allocateDirect
并找到了以下内容:
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
我随后导航到DirectByteBuffer
的构造函数并发现以下方法调用:
Bits.reserveMemory(size, cap);
通过这种方式观察,我们可以看到:
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("Direct buffer memory");
看起来这就是您收到此错误的地方,但现在我们需要弄清楚它的原因。为此,我查看了对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;
}
我对maxMemory
字段很好奇,然后查看了声明它的位置:
private static volatile long maxMemory = VM.maxDirectMemory()
现在我需要查看 VM.java
中的 maxDirectMemory
:
public static long maxDirectMemory() {
return directMemory;
}
最后,让我们看一下directMemory
的声明:
private static long directMemory = 64 * 1024 * 1024;
嘿,看那边!如果您不使用"-XX:MaxDirectMemorySize=<size>"
手动指定,则默认为Runtime.getRuntime().maxMemory()
,这是您设置的堆大小。
既然-Xmx1G
比Integer.MAX_VALUE
字节小,对tryReserveMemory
的调用将永远不会返回true
,这将导致sleeps >= MAX_SLEEPS
,跳出while循环,抛出OutOfMemoryError
。
如果我们查看Runtime.getRuntime().maxMemory()
,那么我们就会明白为什么在未指定最大堆大小时它有效:
public native long maxMemory();