我目前正在用C++编写一个小型模拟器,使用Java作为GUI。为了实现这一点,我从我的C++代码中调用JNI,将数据数组传递给GUI应用程序。但是,由于我在测试运行中调用的量很大,显然在我传递数据的函数中出现了内存泄漏。
在我的程序运行之前:

在我的程序因内存不足而运行并崩溃后:

(请忽略这个程序目前使用的CPU使用,我知道通过JNI重复调用是效率低下的,我还有其他解决办法)
在深入分析了发生了什么之后,我的结论是,不是Java类导致内存泄漏,而是函数中的代码将数据数组传递给Java:
//java.env is the JNIEnv*
//setDisplay_ is a valid non-null jmethodID at runtime
//displayObject is a valid non-null jobject at runtime
void Display::setDisplay(vector<uint32_t>& a)
{
jint* buffer = new jint[a.size()];
for(int i = 0; i < a.size(); i++)
buffer[i] = (jint)a[i];
jintArray par = java.env->NewIntArray(a.size());
java.env->SetIntArrayRegion(par, 0, a.size(), buffer);
java.env->CallVoidMethod(displayObject, setDisplay_, par);
//java.env->ReleaseIntArrayElements(par, buffer, 0);
delete buffer;
}我唯一能看到的导致内存泄漏的函数是jintArray,我完全不知道当它超出范围时会发生什么,所以我只能假设释放缓冲区时的问题。但是,查看使用JNI与数组(例如:这里)的其他人的示例代码,我注意到他们从未发布过他们创建的数组。在深入研究JNI文档时,我偶然发现了Release<NativeType>ArrayElements方法,由于描述,我认为这是我要寻找的:
ReleaseArrayElements例程无效ReleaseArrayElements(JNIEnv *env、ArrayType数组、NativeType *elems、jint模式);通知VM本机代码不再需要访问elems的一系列函数。elems参数是使用相应的GetArrayElements()函数从数组派生的指针。如果有必要,此函数将对elems所做的所有更改复制到原始数组中。mode参数提供有关如何释放数组缓冲区的信息。如果elems不是数组中元素的副本,则模式无效。否则,模式会产生以下影响,如下表所示:
真正给我希望的那句话--这是我特别需要的--
模式参数提供了关于如何释放数组缓冲区的信息。
然而,经过进一步的检查,我不太确定这是我所认为的方法,这在测试中证明了自己,而且它似乎在故障时调用了exit() (因为JNI是如此臭名昭著),而且每次我使用文档中提供的任何模式运行它时,都会发生此故障。
因此,我真正的问题是:当在JNI中从New<PrimitiveType>Array代码创建C++时,如何释放<PrimitiveType>Array的缓冲区?
发布于 2019-01-19 07:28:10
在进一步挖掘之后,我发现了我需要在用ReleaseIntArrayElements创建的数组上调用NewIntArray吗?,@gerbit给出了一个简短的回答:
您必须只发布引用: jintArray像素=env->NewIntArray(宽度*高度);env->DeleteLocalRef(像素)
因此,显然,当使用JNI来调用C++时,您不需要清理您的<PrimitiveType>Array,因为Java会为您处理这个问题。然而,当您从C++调用到Java时,您需要调用DeleteLocalRef()以防止内存泄漏。
https://stackoverflow.com/questions/54264656
复制相似问题