我正在try 测试Java 21的外来功能和内存特性.以下是我的代码:

public static void main(String[] args) {
        // 1. Find foreign function on the C library path
        Linker linker = Linker.nativeLinker();
        SymbolLookup stdlib = linker.defaultLookup();
        MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort").orElseThrow(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_CHAR));
        // 2. Allocate on-heap memory to store four strings
        String[] javaStrings = {"mouse", "cat", "dog", "car"};

        // 3. Use try-with-resources to manage the lifetime of off-heap memory
        try (Arena offHeap = Arena.ofConfined()) {
            // 4. Allocate a region of off-heap memory to store four pointers
            MemorySegment pointers = offHeap.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
            // 5. Copy the strings from on-heap to off-heap
            for (int i = 0; i < javaStrings.length; i++) {
                MemorySegment cString = offHeap.allocateUtf8String(javaStrings[i]);
                pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
                assert cString.getUtf8String(0).length() > 0;
            }
            // 6. Sort the off-heap data by calling the foreign function
            radixsort.invoke(pointers, javaStrings.length, MemorySegment.NULL, '\0');
            // 7. Copy the (reordered) strings from off-heap to on-heap
            for (int i = 0; i < javaStrings.length; i++) {
                MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
                javaStrings[i] = cString.getUtf8String(0);
            }
        } // 8. All off-heap memory is deallocated here
        catch (Throwable e) {
            throw new RuntimeException(e);
        }
        assert Arrays.equals(javaStrings, new String[]{"car", "c∞at", "dog", "mouse"});  // true
    }

但我遇到了这个错误:

Exception in thread "main" java.lang.RuntimeException: java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ heapBase: Optional.empty address:105553153094096 limit: 0 }; new offset = 0; new length = 1

起跑线上:

javaStrings\[i\] = cString.getUtf8String(0);MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);似乎返回一个ByteSize=0的内存段

当我在原始cString上使用cString.getUtf8String(0);时,我没有遇到错误.

似乎pointers.setAtIndexpointers.getAtIndex不保留内存段字节大小.?

推荐答案

似乎pointers.setAtIndex和pointers.getAtIndex不保留内存段字节大小.?

是的,这是正确的.该地址作为普通内存地址存储到堆外内存中.尺寸变小了.因此,当从堆外内存回读地址时,创建的内存段的大小为0.

这一点在javadoc of MemorySegment条中也有解释:

当与 foreign 函数交互时,对于那些 函数来分配一个内存区域并返回指向该区域的指针 区域.利用内存段对内存区进行建模 具有挑战性,因为Java运行时无法深入了解 该地区.Only the address of the start of the region, stored in the pointer, is available.

...

内存分段API使用零长度的内存段来表示:

  • 从外部函数返回的指针;
  • 外部函数传递的指向向上调用存根的指针;以及
  • 从内存段读取的指针(下面将详细介绍).

您的用例是三种用例中的后一种.

必须使用MemorySegment::reinterpret才能显式调整线段的大小.由于C字符串的实际大小未知(由空终止符指示),因此可以将段的大小调整为Long.MAX_VALUE以使其可访问:

// 7. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
    MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
    cString = cString.reinterpret(Long.MAX_VALUE); // <----
    javaStrings[i] = cString.getUtf8String(0);
}

或者,您可以创建具有所需大小的具有target layoutAddressLayout,以避免单独调用reinterpret:

AddressLayout UNBOUNDED_POINTER = ValueLayout.ADDRESS
        .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, ValueLayout.JAVA_BYTE));

然后用它来取消引用:

// 7. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
    MemorySegment cString = pointers.getAtIndex(UNBOUNDED_POINTER, i);
    // no reinterpret needed
    javaStrings[i] = cString.getUtf8String(0);
}

Java相关问答推荐

gitlab ci不会运行我的脚本,因为它需要数据库连接'

Java Swing:初始化身份验证类后未检测到ATM_Interface键事件

FALSE:它应该在什么时候使用?

只需最少的代码更改即可将版本号标记添加到日志(log)

编译多个.Java文件并运行一个依赖于用户参数的文件

Java中的死锁及其重入锁和锁

Java编译器抛出可能未正确初始化的错误?

Arrays.hashcode(int[])为不同的元素提供相同的散列

为什么我的回收视图会显示重复的列表?

如何使用log4j2(Json)记录由";异常引起的所有";?

解析方法";javax/imageio/metadata/IIOMetadata.getAsTree(Ljava/lang/String;)Lorg/w3c/dom/Node时加载约束冲突

来自外部模块的方面(对于Java+Gradle项目)不起作用

如何使用Criteria Builder处理一对多关系中的空值?

我如何为我的Java抵押贷款代码执行加薪操作(&Q)

如何使用MapStrCut转换双向链接

当我在Java中有一个Synchronized块来递增int时,必须声明一个变量Volatile吗?

Java类型推断:为什么要编译它?

设置背景时缺少Android编辑文本下划线

Spring Mapstruct如何获取Lazy初始化实体字段的信息?

Hibernate 命名策略导致 Java Spring Boot 应用程序中出现未知列错误