【问题标题】:Why XX:MaxDirectMemorySize can't limit Unsafe.allocateMemory?为什么 XX:MaxDirectMemorySize 不能限制 Unsafe.allocateMemory?
【发布时间】:2015-04-17 14:28:23
【问题描述】:

下面的代码会分配很大的直接内存但不会导致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 {
            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。 我已经从Java unsafe memory allocation limit 看到了答案,但是 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);
        }
    }
}

为什么限制失败发生在第一个?

【问题讨论】:

    标签: java memory-management


    【解决方案1】:

    正如原始答案所说:Unsafe.allocateMemory()os::malloc 的包装器,它不关心 VM 施加的任何内存限制。

    ByteBuffer.allocateDirect() 将调用此方法,但在此之前,它将调用 Bits.reserveMemory()(在我的 Java 7 版本中:DirectByteBuffer.java:123)检查进程的内存使用情况并抛出您提到的异常。

    【讨论】:

    • 感谢您的回答!
    【解决方案2】:

    错误来自Bits.reserveMemory,它在调用allocateDirect时在unsafe.allocateMemory(size)之前调用。

    reserveMemory 方法进行此验证:

    synchronized (Bits.class) {
        if (totalCapacity + cap > maxMemory)
            throw new OutOfMemoryError("Direct buffer memory");
        reservedMemory += size;
        totalCapacity += cap;
        count++;
    }
    

    如果所需的分配高于从检索到的maxMemory,则会引发错误

    maxMemory = VM.maxDirectMemory();
    

    直接调用allocateMemory 将执行本机方法,并且不会验证最大容量(这解释了为什么您在第一个sn-p 中没有收到错误)这是--XX:MaxDirectMemorySize 的主要目标,如解释在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;
    }
    

    值得一提的是,您的第一个 sn-p 实现并不是一个好习惯。 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) {
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-26
      • 2013-09-28
      • 2016-04-16
      • 1970-01-01
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      相关资源
      最近更新 更多