2

我需要使用 GC 无法控制的可用 RAM 中的空间。我读了几篇同样的文章,介绍了两种方法。它们在以下代码中指定。

package com.directmemory;

导入 java.lang.reflect.Field;导入 java.nio.ByteBuffer;

导入 sun.misc.Unsafe;

公共类 DirectMemoryTest {

public static void main(String[] args) {

    //Approach 1
    ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(8);
    directByteBuffer.putDouble(1.0);
    directByteBuffer.flip();
    System.out.println(directByteBuffer.getDouble());

    //Approach 2
    Unsafe unsafe = getUnsafe();
    long pointer = unsafe.allocateMemory(8);
    unsafe.putDouble(pointer, 2.0);
    unsafe.putDouble(pointer+8, 3.0);

    System.out.println(unsafe.getDouble(pointer));
    System.out.println(unsafe.getDouble(pointer+8));
    System.out.println(unsafe.getDouble(pointer+16));
}

public static Unsafe getUnsafe() {
    try {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe) f.get(null);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

}

我有一些问题

1)为什么我应该注意代码中提到的方法1,因为根据我的理解 ByteBuffer.allocateDirect() 不能返回一个存储容量大于 2GB 的缓冲区?因此,如果我的要求是存储 3 GB 的数据,我必须创建一个新缓冲区并将数据存储在那里,这意味着除了存储数据之外,我还有识别相应缓冲区的额外责任(在 'n 列表之外) ' 缓冲区),它维护指向直接内存的指针。

2)方法2不是比方法1快一点,因为我不必先找到缓冲区然后再找到数据,我只需要一个对象字段的索引机制并使用getDouble / getInt方法并传递绝对值地址 ?

3)直接内存的分配(说堆内存是否正确?)与PID有关吗?如果在一台机器上,我有 2 个 java 进程,PID 1 和 PID 2 中的 allocateMemory 调用让我永远不会使用相交的内存块?

4) 为什么最后一个 sysout 语句没有导致 0.0 ?这个想法是每个双精度都使用 8 个字节,所以我将 1.0 存储在 allocateMemory 返回的地址,比如地址 = 1,地址 1+8 的 2.0 是 9,然后停止。那么默认值不应该是 0.0 吗?

4

1 回答 1

7

需要考虑的一点是 sun.misc.Unsafe 不是受支持的 API。它将被其他东西取代(http://openjdk.java.net/jeps/260

1) 如果您的代码必须在 Java 8 到 Java 10(及更高版本)上不变地运行,使用 ByteBuffers 的方法 1 是可行的方法。

如果你准备好用 Java 9 / Java 10 中的任何替换来替换 sun.misc.Unsafe 的使用,你可能会选择 sun.misc.Unsafe。

2)对于超过 2 GB 的非常大的数据结构,由于方法 1 中必要的额外间接性,方法 2 可能会更快。但是,如果没有可靠的(微)基准,我不会在它上面打赌。

3)分配的内存总是绑定到当前运行的JVM上。因此,如果在同一台机器上运行两个 JVM,您将不会获得相交的内存。

4) 您正在分配 8 个字节的未初始化内存。您现在可以合法访问的唯一内存量是 8 个字节。对于超出分配大小的内存,不作任何保证。

4a) 您在分配的内存 ( unsafe.putDouble(pointer+8, 3.0);) 之外写入 8 个字节,这已经导致内存损坏,并可能导致下一次内存分配时 JVM 崩溃。

4b) 您正在读取超出分配内存的 16 个字节,这(取决于您的处理器架构和操作系统以及之前的内存使用情况)可能会导致 JVM 立即崩溃。

于 2015-12-26T10:09:57.893 回答