为什么XX:MaxDirectMemorySize不能限制Unsafe.allocateMemory?
Why XX:MaxDirectMemorySize can't limit Unsafe.allocateMemory?
下面的代码将分配大量的直接内存但不会导致java.lang.OutOfMemoryError: 直接缓冲内存 :
//JVM args: -Xms10m -Xmx10m -XX:MaxDirectMemorySize=10m
public class DirectMemoryOOM {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredFields()[0];
f.setAccessible(true);
Unsafe us = (Unsafe) f.get(null);
long size = 1024 * 1024 * 1024;
while (true) {
long p = us.allocateMemory(size);
for (int i = 0; i < size; i++) {
us.putByte(p + i, Byte.MAX_VALUE);
}
}
}
}
但是下面的代码会得到java.lang.OutOfMemoryError: Direct buffer memory。
我已经看到 的答案,但是 ByteBuffer.allocateDirect 是使用 Unsafe.allocateMemory()
实现的
//JVM args: -Xms10m -Xmx10m -XX:MaxDirectMemorySize=10m
public class DirectMemoryOOM {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
int size = 1024 * 1024;
System.out.println(sun.misc.VM.maxDirectMemory());
while (true) {
ByteBuffer.allocateDirect(size);
}
}
}
为什么第一个限制失败?
正如原始答案所说:Unsafe.allocateMemory()
是 os::malloc
的包装器,它不关心 VM 强加的任何内存限制。
ByteBuffer.allocateDirect()
将调用此方法,但在此之前,它会调用 Bits.reserveMemory()
(在我的 Java 7 版本中:DirectByteBuffer.java:123
)检查内存使用情况过程并抛出您提到的异常。
错误来自调用 allocateDirect
时在 unsafe.allocateMemory(size)
之前调用的 Bits.reserveMemory
。
reserveMemory
方法进行此验证:
synchronized (Bits.class) {
if (totalCapacity + cap > maxMemory)
throw new OutOfMemoryError("Direct buffer memory");
reservedMemory += size;
totalCapacity += cap;
count++;
}
如果所需分配高于从
检索到的maxMemory
,则会抛出错误
maxMemory = VM.maxDirectMemory();
直接调用 allocateMemory
将继续本机方法并且不会验证最大容量(这解释了为什么您在第一个片段中没有收到错误)这是 [=20 的主要目标=] 如 reserveMemory
中的评论所述
// -XX:MaxDirectMemorySize limits the total capacity rather than the
// actual memory usage, which will differ when buffers are page
// aligned.
if (cap <= maxMemory - totalCapacity) {
reservedMemory += size;
totalCapacity += cap;
count++;
return;
}
值得一提的是,您的第一个代码段实施并不是一个好的做法。 Bits.java
中的注释指定只要分配直接内存,就应始终调用 reserveMemory
:
// 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, int cap) {
下面的代码将分配大量的直接内存但不会导致java.lang.OutOfMemoryError: 直接缓冲内存 :
//JVM args: -Xms10m -Xmx10m -XX:MaxDirectMemorySize=10m
public class DirectMemoryOOM {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredFields()[0];
f.setAccessible(true);
Unsafe us = (Unsafe) f.get(null);
long size = 1024 * 1024 * 1024;
while (true) {
long p = us.allocateMemory(size);
for (int i = 0; i < size; i++) {
us.putByte(p + i, Byte.MAX_VALUE);
}
}
}
}
但是下面的代码会得到java.lang.OutOfMemoryError: Direct buffer memory。
我已经看到
//JVM args: -Xms10m -Xmx10m -XX:MaxDirectMemorySize=10m
public class DirectMemoryOOM {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
int size = 1024 * 1024;
System.out.println(sun.misc.VM.maxDirectMemory());
while (true) {
ByteBuffer.allocateDirect(size);
}
}
}
为什么第一个限制失败?
正如原始答案所说:Unsafe.allocateMemory()
是 os::malloc
的包装器,它不关心 VM 强加的任何内存限制。
ByteBuffer.allocateDirect()
将调用此方法,但在此之前,它会调用 Bits.reserveMemory()
(在我的 Java 7 版本中:DirectByteBuffer.java:123
)检查内存使用情况过程并抛出您提到的异常。
错误来自调用 allocateDirect
时在 unsafe.allocateMemory(size)
之前调用的 Bits.reserveMemory
。
reserveMemory
方法进行此验证:
synchronized (Bits.class) {
if (totalCapacity + cap > maxMemory)
throw new OutOfMemoryError("Direct buffer memory");
reservedMemory += size;
totalCapacity += cap;
count++;
}
如果所需分配高于从
检索到的maxMemory
,则会抛出错误
maxMemory = VM.maxDirectMemory();
直接调用 allocateMemory
将继续本机方法并且不会验证最大容量(这解释了为什么您在第一个片段中没有收到错误)这是 [=20 的主要目标=] 如 reserveMemory
// -XX:MaxDirectMemorySize limits the total capacity rather than the
// actual memory usage, which will differ when buffers are page
// aligned.
if (cap <= maxMemory - totalCapacity) {
reservedMemory += size;
totalCapacity += cap;
count++;
return;
}
值得一提的是,您的第一个代码段实施并不是一个好的做法。 Bits.java
中的注释指定只要分配直接内存,就应始终调用 reserveMemory
:
// 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, int cap) {