Java 21外部函数与内存特性测试遇IndexOutOfBoundsException问题求助
解决Java 21 Foreign Function and Memory中MemorySegment大小丢失的问题
问题根源
ValueLayout.ADDRESS仅存储内存地址值,不会附带MemorySegment的大小元数据。当你通过pointers.setAtIndex(ValueLayout.ADDRESS, i, cString)写入指针时,实际上只保存了地址,原始cString的内存范围信息被丢弃。后续用getAtIndex取出的Segment默认大小为0,调用getUtf8String(0)自然会触发越界异常。
解决方案
1. 手动关联内存范围
从指针数组取出地址后,使用reinterpret(long byteSize)方法给Segment手动绑定正确的内存大小。需要预先记录每个C字符串的实际长度(包含末尾的null终止符):
// 假设已提前记录每个字符串的长度(含null终止符)为stringSizes[i] MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i) .reinterpret(stringSizes[i]); javaStrings[i] = cString.getUtf8String(0);
如果是通过SegmentAllocator.allocateUtf8String()创建的C字符串,其长度为原Java字符串长度+1(null终止符),可以直接基于原字符串长度计算。
2. 维护大小元数据数组
在存储指针的同时,单独用一个数组记录每个C字符串的内存大小,后续取出指针时通过该数组获取对应大小:
try (Arena arena = Arena.ofAuto()) { int strCount = 3; MemorySegment pointers = arena.allocate(ValueLayout.ADDRESS, strCount); String[] originalStrings = {"hello", "world", "test"}; int[] strSizes = new int[strCount]; // 初始化指针和大小数组 for (int i = 0; i < strCount; i++) { MemorySegment cStr = arena.allocateUtf8String(originalStrings[i]); strSizes[i] = originalStrings[i].length() + 1; // 包含null终止符 pointers.setAtIndex(ValueLayout.ADDRESS, i, cStr); } // 转换为Java字符串 String[] javaStrings = new String[strCount]; for (int i = 0; i < strCount; i++) { MemorySegment cStr = pointers.getAtIndex(ValueLayout.ADDRESS, i) .reinterpret(strSizes[i]); javaStrings[i] = cStr.getUtf8String(0); } }
3. 利用结构化布局存储指针与大小
如果场景复杂,可以定义结构体布局,将指针和大小绑定存储,避免单独维护数组:
// 定义结构体布局:地址 + 大小 GroupLayout strPtrLayout = MemoryLayout.structLayout( ValueLayout.ADDRESS.withName("ptr"), ValueLayout.JAVA_INT.withName("size") ); try (Arena arena = Arena.ofAuto()) { int strCount = 3; MemorySegment ptrArray = arena.allocate(strPtrLayout, strCount); String[] originalStrings = {"hello", "world", "test"}; // 写入结构体数据 for (int i = 0; i < strCount; i++) { MemorySegment cStr = arena.allocateUtf8String(originalStrings[i]); long offset = strPtrLayout.byteSize() * i; ptrArray.set(ValueLayout.ADDRESS, offset, cStr); ptrArray.set(ValueLayout.JAVA_INT, offset + ValueLayout.ADDRESS.byteSize(), originalStrings[i].length() + 1); } // 读取并转换 String[] javaStrings = new String[strCount]; for (int i = 0; i < strCount; i++) { long offset = strPtrLayout.byteSize() * i; MemorySegment cStr = ptrArray.get(ValueLayout.ADDRESS, offset) .reinterpret(ptrArray.get(ValueLayout.JAVA_INT, offset + ValueLayout.ADDRESS.byteSize())); javaStrings[i] = cStr.getUtf8String(0); } }
4. 确认内存分配的正确性
如果是从外部C函数获取的指针,必须确保拿到字符串的实际长度(可以通过C的strlen函数调用获取),再用reinterpret绑定大小。如果是自行分配的内存,务必使用SegmentAllocator的方法(如allocateUtf8String),确保内存包含null终止符且大小计算正确。
内容的提问来源于stack exchange,提问作者Abdelfettah Ghazi
相关产品推荐
相关产品推荐

