You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI中jShortArray内存释放问题:如何免拷贝安全复用内存?

解决JNI中无需拷贝安全释放内存的问题

首先得明确为什么直接赋值会触发堆损坏:你通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:22:32