跳到内容
argsno
返回

模拟直接内存溢出

最近在重新阅读一遍《深入理解 Java 虚拟机》,因为现在一直用着Java 8环境,就用Java 8跑一下书上的例子。不过,书上是基于Java 6/7,经过Java 8的改进,很多例子都没法测试,主要是方法区/永久区的例子,在Java 8中是元数据区。不过本文是模拟直接内存OOM的例子。

书上的例子调用unsafe.allocateMemory()方法分配内存,但是没有触发OOM,程序正常运行。

/**
 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
 * @author zzm
 */
public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(_1MB);
        }
    }
}

在StackOverflow上找到别人的提问:Why XX:MaxDirectMemorySize can’t limit Unsafe.allocateMemory?,遇到了和我一样的问题。下面说一下具体的原因:

Unsafe.allocateMemory()是系统调用的os::malloc一个包装,并没有关心VM要求的内存限制,所以也就绕过了MaxDirectMemorySize的限制。 而ByteBuffer.allocateDirect()在会在调用这个方法之前,调用Bits.reserveMemory(),这个方法将检查进程的内存占用情况并抛出异常。

修改后的代码如下:

/**
 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
 * @author zzm
 */
public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        List<ByteBuffer> list = new ArrayList<>();
        while (true) {
//            unsafe.allocateMemory(_1MB);
            list.add(ByteBuffer.allocateDirect(_1MB));
        }
    }
}

输出:

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:694)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
	at com.argsno.understaingjvm.ch2.DirectMemoryOOM.main(DirectMemoryOOM.java:24)

分享这篇文章:

上一篇文章
算法精进之 k-d树
下一篇文章
Future