Buffer 与 Unsafe - 在 JVM 之外

Buffer vs Unsafe - Outside JVM

我需要在 GC 无法控制的可用 RAM 中使用 space。我读了几篇关于这方面的文章,其中介绍了两种方法。它们在以下代码中指定。

package com.directmemory;

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

导入sun.misc.Unsafe;

public class 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() 不能 return 给我一个存储容量大于 2GB 的缓冲区?因此,如果我的要求是存储 3 GB 的数据,我必须创建一个新缓冲区并将数据存储在那里,这意味着除了存储数据之外,我还有责任识别相应的缓冲区(在 [= 列表之外) 32=] buffers) 维护指向直接内存的指针。

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

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

4) 为什么最后一个 sysout 语句的结果不是 0.0?这个想法是每个 double 使用 8 个字节,所以我将 1.0 存储在地址 returned by allocateMemory 说地址 = 1,地址 1+8 的 2.0 即 9,然后停止。那么默认值不应该是 0.0 吗?

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

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

如果您准备好将 sun.misc.Unsafe 的使用替换为 Java 9 / Java 10 中的任何替换,您可以使用 sun.misc.Unsafe。

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

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

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

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

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