Rust原生库被JVM调用时函数随机崩溃问题求助
问题概述
Rust实现的数组压缩逻辑在单元测试中完全正常,但通过JVM(使用jnr-ffi)调用时,程序无恐慌/调试信息直接崩溃,退出码为-1073740940 (0xC0000374)。具体表现为:create_bitvec函数的"made it here"日志能正常输出且数组内容正确,但create函数中"Got here 1"日志输出前就崩溃。
相关代码
Rust侧核心实现
create_bitvec函数
fn create_bitvec(data: Box<[u8]>) -> [u8; 49152] { let mut bit_vector = [0u8; 49152]; for (index, &value) in data.iter().enumerate() { let byte_index = (index * 4) / 8; let bit_index = (index * 4) % 8; let value_mask = 0b00001111 << bit_index; let shifted_value = (value << bit_index) & value_mask; bit_vector[byte_index] &= !value_mask; bit_vector[byte_index] |= shifted_value; } debug!("made it here. {:?}", bit_vector); bit_vector }
create函数
pub fn create(data: Box<[u8]>) -> Chunk { assert_eq!(data.len(), FULL, "Data length doesn't match!"); let bitvec = Chunk::create_bitvec(data); debug!("Got here 1!"); let c = Chunk { data: Chunk::create_rle(bitvec), }; debug!("Got here 2: {:?} {}!", c.data, c.data.len()); c }
暴露给JVM的C接口
static CHUNK_STATE: Mutex<Cell<Option<ChunkManager>>> = Mutex::new(Cell::new(None)); #[no_mangle] pub extern "C" fn chunk_build(x: i64, y: i64, arr: *const u8) { info!("Building chunk: {}, {}", x, y); unsafe { CHUNK_STATE.lock().get_mut().as_mut().expect("Not initialized!").build((x, y), Box::from_raw(slice::from_raw_parts_mut(arr as *mut u8, WIDTH * WIDTH * HEIGHT))); } }
Java侧调用代码
NativeLib接口
public interface NativeLib { void chunk_build(int x, int y, byte[] arr); }
加载库逻辑
import jnr.ffi.LibraryLoader; public class Natives { public static NativeLib INSTANCE; public static void init() { INSTANCE = LibraryLoader.create(NativeLib.class).load("C:\\Users\\*\\*\\*\\nativelib\\target\\release\\pathlib.dll"); INSTANCE.init(); } }
已尝试的排查步骤
- 验证输入数组长度符合预期
- 确认输入数据适配
u8类型 - Rust单元测试覆盖核心逻辑,无异常
问题原因分析
错误码0xC0000374对应Windows系统的堆损坏(Heap Corruption),结合代码细节,核心问题集中在以下几点:
非法内存释放
在chunk_build中,使用Box::from_raw(slice::from_raw_parts_mut(arr as *mut u8, ...))将JVM管理的byte[]内存转换为Rust的Box<[u8]>。Rust的Box会在生命周期结束时自动调用内存释放逻辑,但这块内存属于JVM堆,并非Rust分配器管理,直接释放会破坏JVM的堆结构,导致后续操作崩溃。参数类型不匹配
Java端传递的x、y是32位int类型,但Rust侧定义为64位i64。虽然日志中能输出x、y的值,但类型不匹配可能导致栈帧错乱,间接引发内存访问异常。大数组栈分配风险
create_bitvec返回的[u8; 49152]是栈上分配的数组(48KB),虽然Windows默认栈大小为1MB,但JVM调用原生函数时的栈空间可能受限,存在栈溢出隐患。
解决办法
1. 修复非法内存释放问题
不要直接将JVM内存转换为Rust的Box,而是复制数据到Rust管理的内存中:
#[no_mangle] pub extern "C" fn chunk_build(x: i64, y: i64, arr: *const u8) { info!("Building chunk: {}, {}", x, y); unsafe { // 先从JVM内存中复制数据到Rust管理的Vec/Box let slice = slice::from_raw_parts(arr, WIDTH * WIDTH * HEIGHT); let data = slice.to_vec().into_boxed_slice(); CHUNK_STATE.lock().get_mut().as_mut().expect("Not initialized!").build((x, y), data); } }
2. 统一参数类型
确保Java与Rust的参数类型匹配,任选一种方案:
- Rust侧改为32位整数:
pub extern "C" fn chunk_build(x: i32, y: i32, arr: *const u8) { // ... 逻辑不变 } - Java侧改为64位长整数:
public interface NativeLib { void chunk_build(long x, long y, byte[] arr); }
3. 避免大数组栈分配
将create_bitvec的返回类型改为堆分配的Box<[u8]>,消除栈溢出风险:
fn create_bitvec(data: Box<[u8]>) -> Box<[u8]> { let mut bit_vector = vec![0u8; 49152].into_boxed_slice(); for (index, &value) in data.iter().enumerate() { let byte_index = (index * 4) / 8; let bit_index = (index * 4) % 8; let value_mask = 0b00001111 << bit_index; let shifted_value = (value << bit_index) & value_mask; bit_vector[byte_index] &= !value_mask; bit_vector[byte_index] |= shifted_value; } debug!("made it here. {:?}", bit_vector); bit_vector }
验证建议
- 先修复内存释放问题,这是堆损坏的核心原因
- 测试时启用Rust的调试断言(
debug模式编译),更容易捕获内存越界等问题 - 使用Windows的调试工具(如WinDbg)附加到JVM进程,查看崩溃时的调用栈,进一步定位问题
内容的提问来源于stack exchange,提问作者hello planet

