java - `ByteBuffer.allocateDirect` 和 Xmx

标签 java memory-management

根据 various sources (尽管在 JavaDoc 中没有特别提到),ByteBuffer.allocateDirect 分配主 JVM 堆之外的内存。我可以确认使用 Java Mission Control,看到调用 ByteBuffer n = ByteBuffer.allocateDirect(Integer.MAX_VALUE) 的程序没有使用太多 Java 堆内存:

enter image description here

但是,当限制 JVM 堆内存时,此堆外内存分配将停止工作。例如,当我使用 -Xmx1g 选项运行 JVM 时,allocateDirect 调用导致以下异常:Exception in thread "main"java.lang.OutOfMemoryError:直接缓冲内存。我不完全理解这个 JVM 选项如何与堆外直接内存分配相关,因为 - 根据 documentation - -Xmx 选项设置 Java 堆空间大小。如果我使用 getUnsafe().allocateMemory(Integer.MAX_VALUE); 分配内存,则内存分配成功。我的 JVM 如下:

java version "10" 2018-03-20 Java(TM) SE Runtime Environment 18.3 (build 10+46) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)

XmxByteBuffer.allocateDirect 之间的这种行为是否符合预期?

编辑:似乎有一个(不可复制的)bug在 JDK 1.7 中具有与上述相同的行为。那么这是一个错误吗?

最佳答案

我不得不继续寻找寻宝游戏来寻找原因,但是给你!

首先,我查看了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;
    }
}

// no luck
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();

现在我必须查看 maxDirectMemoryVM.java 内:

public static long maxDirectMemory() {
    return directMemory;
}

最后我们来看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;

嘿,看那个!如果您不使用 "-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();

关于java - `ByteBuffer.allocateDirect` 和 Xmx,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50499238/

相关文章:

java - 如果您不为接口(interface)指定访问修饰符,那么该接口(interface)将具有默认访问权限,这是真的吗

java - 从链表 LUT 中删除节点?

c - 如何在不破坏严格别名的情况下在一个 malloc 调用中为数组和结构分配内存?

java - 有没有JDK增量升级之类的东西?

java - 创建一个给定宽度的按钮

c++ - 将二进制文件读入堆栈而不是堆

c++ - C++ 中的 3 维 vector -传递给函数之前的部分大小定义

iPhone:对象保留其委托(delegate)是否有意义?

Java 进程流

java - 将 Java 应用程序编写为 jar 和 apk