Java是否可以写入/读取已释放的堆外内存?

时间:2018-10-19 03:07:06

标签: java unsafe

这确实让我感到惊讶,我在玩Java Unsafe。基本上我正在测试的是

Allocate unsafe memory -> free the memory -> Write to the freed memory

当我访问已释放的内存时,我曾期望看到某种分段错误错误,但令人惊讶的是,未引发任何错误/异常。

我的代码是:

protected static final Unsafe UNSAFE;
static {
    try {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        UNSAFE = (Unsafe) field.get(null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
@Test
public void test() {
    long ptr = UNSAFE.allocateMemory(1000);
    UNSAFE.freeMemory(ptr);
    UNSAFE.putOrderedLong(null, ptr, 100L);
}

我的问题是,如果是这样,那为什么我们在Unsafe中需要freeMemory()函数呢?真的是为了什么?

2 个答案:

答案 0 :(得分:2)

仅当您读取未分配页面的地址或写入只读或未分配页面的内存时,您才会收到信号。

当您分配较小的内存区域时,它将从本机内存池中获取该内存,释放时通常不会将其释放回操作系统。

如果释放内存,然后再分配,则该内存可用于其他用途。继续使用旧地址可能会导致损坏。

答案 1 :(得分:1)

这可能已完全关闭,但无论如何我都会发布。

在我看来,这类似于它在C中的工作方式(如@ John3136建议)。查看“官方”文档(http://www.docjar.com/html/api/sun/misc/Unsafe.java.html),很显然,您分配的内存是本机内存。这意味着它在JVM内存中,但在OS自身分配给JVM的堆中。

当OS分配了一块内存时,它会为此内存分配一个特定的地址范围,并且(除了静态内存和代码等其他几件事之外)堆从底部开始增长,而堆栈从顶部开始增长地址空间(希望这是熟悉的)。因此,如果在堆中分配了内存,并且再次引用它,那么您就不可能超出操作系统分配的范围。实际上,我敢肯定,如果您尝试使用UNSAFE.putOrderedLong(null, ptr+4, 100L)的计算机,它仍然可以正常工作。现在,确切存储在其中的内容甚至对于OS本身都是一个谜,但是由于UNSAFE的运行水平如此之低,因此绝对有可能。