JNI中jShortArray内存释放问题:如何免拷贝安全复用内存?
首先得明确为什么直接赋值会触发堆损坏:你通过GetShortArrayElements拿到的body指针,不是你用new或malloc分配的堆内存,它是JVM内部管理的内存区域(可能是原始数组的直接指针,也可能是JVM临时拷贝出来的内存)。所以你不能用delete[]或者free去释放它,必须用JNI提供的对应函数来回收,否则就会破坏JVM的内存管理逻辑,导致堆损坏错误。
那怎么不用拷贝就能安全使用并释放这块内存呢?这里有两个可行的方案:
方案1:直接使用JNI返回的指针,用JNI函数释放
既然t_data原本是用来持有数组指针,那你可以直接让t_data指向body(类型转换要保证安全),但绝对不能用delete[] t_data,而是在你不再需要这个内存的时候,调用ReleaseShortArrayElements通知JVM完成内存回收:
jshortArray val = (jshortArray)(m_pJVMInstance->m_pEnv->CallStaticObjectMethod(m_imageJ_cls, method_id, arr, (jint)t, (jint)c)); jsize len = m_pJVMInstance->m_pEnv->GetArrayLength(val); jshort* body = m_pJVMInstance->m_pEnv->GetShortArrayElements(val, nullptr); // 直接赋值,unsigned short*和jshort*都是16位类型,转换安全 t_data = reinterpret_cast<unsigned short int*>(body); // 当你不再需要t_data时,调用JNI的释放函数(必须保留val引用直到此时) m_pJVMInstance->m_pEnv->ReleaseShortArrayElements(val, body, 0); // 释放后t_data变为野指针,记得置空避免误用 t_data = nullptr; // 最后可以释放JNI的局部引用 m_pJVMInstance->m_pEnv->DeleteLocalRef(val);
这里要注意ReleaseShortArrayElements的第三个参数:
- 传
0表示更新原始Java数组并释放JVM分配的内存(如果是拷贝的话) - 传
JNI_ABORT表示放弃更新原始数组,直接释放内存
方案2:用GetPrimitiveArrayCritical提升性能(谨慎使用)
如果你的操作是纯计算逻辑,不会调用任何JNI函数或阻塞操作,可以用GetPrimitiveArrayCritical获取更直接的指针(JVM会尽量返回原始数组的指针,减少拷贝概率),同样用对应函数释放:
jshortArray val = (jshortArray)(m_pJVMInstance->m_pEnv->CallStaticObjectMethod(m_imageJ_cls, method_id, arr, (jint)t, (jint)c)); jsize len = m_pJVMInstance->m_pEnv->GetArrayLength(val); jshort* body = static_cast<jshort*>(m_pJVMInstance->m_pEnv->GetPrimitiveArrayCritical(val, nullptr)); t_data = reinterpret_cast<unsigned short int*>(body); // 使用完后立即释放,期间不能调用任何可能阻塞JVM的操作 m_pJVMInstance->m_pEnv->ReleasePrimitiveArrayCritical(val, body, 0); t_data = nullptr; m_pJVMInstance->m_pEnv->DeleteLocalRef(val);
⚠️ 重要提醒:使用GetPrimitiveArrayCritical的期间,绝对不能调用任何可能导致JVM挂起或阻塞的函数(比如其他JNI调用、I/O操作),否则极易触发死锁。
为什么拷贝方案是安全的?
你之前的拷贝方案安全,是因为t_data指向的是你自己用new分配的内存,所以可以用delete[]正常释放。但大图像拷贝必然耗时,所以优先用上面的方案1或2,从根源避免拷贝操作。
总结一下:核心原则就是JNI分配的内存必须用JNI的释放函数处理,不能用C++的delete/free,只要遵循这个规则,就能不用拷贝直接使用指针,同时彻底避免堆损坏问题。
内容的提问来源于stack exchange,提问作者Remaldeep

