- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
根据 various sources (尽管在 JavaDoc 中没有特别提到),ByteBuffer.allocateDirect
分配主 JVM 堆之外的内存。我可以确认使用 Java Mission Control,看到调用 ByteBuffer n = ByteBuffer.allocateDirect(Integer.MAX_VALUE)
的程序没有使用太多 Java 堆内存:
但是,当限制 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)
Xmx
和 ByteBuffer.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();
现在我必须查看 maxDirectMemory
在 VM.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/
我有一个空的 byteBuffer 分配为 data = ByteBuffer.allocateDirect(layerSize(0, faces - 1, 0, levels - 1) * laye
虽然存在 ByteBuffer.put(ByteBuffer) 方法,但 ByteBuffer.get(ByteBuffer) 似乎丢失了?我应该如何从较大的 ByteBuffer 读取较小的 Byt
我在 Dart Web 应用程序中加载二进制文件,其中包含压缩音频数据作为二进制数据的一部分。当数据通过 http 请求加载时,它作为一个 ByteBuffer 出现,我理解它是 Dart 对 JS
假设我有一个列表/数组列表或字节缓冲区数组(List 或 ByteBuffer[]) 是否可以由此直接从上述数组中获取字节,而无需遍历所有项或计算它们的总大小?我正在寻找这样的东西: ByteBuff
我有一个或多个 ByteBuffer,其中包含一条消息的部分内容。现在我想阅读此消息,但我不想将 N ByteBuffer 复制到一个中。我的解析器需要一个包含完整消息的 ByteBuffer,但我的
如果已知它是其他缓冲区? 我知道这可以通过非直接数组支持的 ByteBuffer 使用 arrayOffset() 方法来完成,如下所示: int getRelativeBufferOffset(By
如何在 Dart 中将 ByteBuffer 的字节快速复制到另一个更大的 ByteBuffer(以非零偏移量)? 有一些缓慢的方法可以做到这一点。一种是将每个都转换为 Uint8List 并一次复制
嘿嘿, ByteBuffers 以及 netty 的 ByteBuff 使用索引来存储它们当前“所在”的位置。在我的应用程序开始时,我加载 ByteBuffers/ByteBuffs 中的多个文件以便
我知道 flip() 将当前缓冲区位置设置为 0,并将限制设置为上一个缓冲区位置,而 rewind() 只是将当前缓冲区位置设置为 0。 在下面的代码中,我使用 rewind() 或 flip() 得
我有一个数组 ByteBuffer s(实际上代表整数)。我想在数组中分离唯一和非唯一的 ByteBuffer(即整数)。因此我使用这种类型的 HashSet: HashSet columnsSet
我的代码中有一个 java.nio.ByteBuffer: ByteBuffer bb = ByteBuffer.allocateDirect(1024); ... 我希望能够将其替换为 ByteBu
我需要将代码从 Java 移植到 C#。在Java代码中,使用了方法“ByteBuffer.flip()”和“ByteBuffer.slice”,不知道怎么翻译。 我读过这个问题 ( An equiv
以下 Java 代码可以编译,但在运行时会出错: # javac ByteBufTest.java # java ByteBufTest Exception in thread "main" java
我有一个方法如下,它已经正常运行了很长时间: private String loadFromFile(){ RandomAccessFile inFile = null; FileCh
用户在测试中遇到了此崩溃。我的第一个猜测是这与内存有关,但除此之外我没有什么可做的。更深入地研究代码,我认为这可能是主线程问题,但看起来监听器在后台线程上被删除,所以我怀疑这就是原因。 我认为在应用程
以下代码(此为简化版)以前在jdk1.6中运行良好,现在在jdk 1.7下断言失败。 ByteBuffer buffer = ...; buffer.mark(); char c = (char) b
我有一个 10MB 的二进制文件。我需要以不同大小的 block (例如 300、273 字节)读取它。为了阅读,我使用 FileChannel 和 ByteBuffer。现在,对于每次读取迭代,我都
我想要做的是获取一个十进制整数,将其转换为十六进制,然后分隔字节。 据我了解,ByteBuffer 是实现此目的的最佳方法。整数不会超过65535,因此十六进制数保证为2个字节。例如,我有一个整数 4
我正在写一些使用 ByteBuffer 的东西s。在 docs of the API它说 There is no way to free a buffer explicitly (without JV
我正在尝试读取一个短数组,转换其元素并将它们放入字节数组中。我使用 ByteBuffer 并收回此异常: 线程“main”中的异常 java.lang.IndexOutOfBoundsExceptio
我是一名优秀的程序员,十分优秀!