I am attempting to test the Foreign Function and Memory features of Java 21. Here is my code :
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
}
but I am encountering this error :
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
At the line :
javaStrings\[i\] = cString.getUtf8String(0);
It seems that MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i); returns a MemorySegment with a byteSize = 0
When I use cString.getUtf8String(0); on the original cString, I do not encounter an error.
It seems that pointers.setAtIndex and pointers.getAtIndex do not preserve the MemorySegment byteSize. ?
Yes, that is correct. The address is stored into off-heap memory as a plain memory address. The size is dropped. So, when reading an address back from off-heap memory, the created memory segment has size 0.
This is explained in the javadoc of MemorySegment as well:
Your use case is the latter of the three.
You have to use
MemorySegment::reinterpretto resize the segment explicitly. Since for a C string the actual size is not known (it's indicated by a null terminator), you can resize the segment toLong.MAX_VALUEto make it accessible:Alternatively, you can create an
AddressLayoutwith a target layout that has the needed size, to avoid the separate call toreinterpret:And then use that to do the dereference: