所有这些都与新的 JDK 17 相关。
我正在尝试将堆上字节数组转换为 MemorySegment 并将其传递给本机函数。我创建了简单的示例代码来显示这一点:
final CLinker cLinker = CLinker.getInstance();
// int strlen(const char *str);
final Optional<MemoryAddress> oSymbolAddress = CLinker.systemLookup().lookup("strlen");
final MethodHandle mh = cLinker.downcallHandle(oSymbolAddress.get(),
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER));
out.println("I found this method handle: " + mh);
final byte[] ba = new byte[100];
ba[0] = 'h';
ba[1] = 'e';
ba[2] = 'l';
ba[3] = 'l';
ba[4] = 'o';
ba[5] = 0;
final MemorySegment stringSegment = MemorySegment.ofArray(ba);
final int result = (Integer) mh.invoke(stringSegment.address());
out.println("The length of the string is: " + result);
它试图运行但它抛出:
Exception in thread "main" java.lang.UnsupportedOperationException: Not a native address
at jdk.incubator.foreign/jdk.internal.foreign.MemoryAddressImpl.toRawLongValue(MemoryAddressImpl.java:91)
如果不是使用MemorySegment.ofArray(ba)
,我使用这个:
final MemorySegment stringSegment = MemorySegment.allocateNative(100, newImplicitScope());
stringSegment.asByteBuffer().put(ba);
它有效并给出了预期的答案(5)。
我查看了这个函数MemoryAddressImpl.java
,我可以看到:
@Override
public long toRawLongValue() {
if (segment != null) {
if (segment.base() != null) {
throw new UnsupportedOperationException("Not a native address");
}
segment.checkValidState();
}
return offset();
}
显然segment.base()
是返回null。
我真的不明白发生了什么事。我认为 Project Panama 的主要优势之一是本机代码可以访问堆内存,以避免复制。我当然可以做一个allocateNative()
然后将字节数组复制到其中,但这是我认为应该避免的副本。
对此有什么想法吗?我做错了什么或误解了如何使用堆内存?