使用 JNI 从 C 调用 java 代码时出现内存泄漏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1340938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Memory leak when calling java code from C using JNI
提问by ata
I have a C program that stores some object in java store using JNI. (Before someone ask, using java store is a requirment here and I have to write a client in C which would be able to add and retrieve objects from this store).
我有一个 C 程序,它使用 JNI 在 java 商店中存储一些对象。(在有人问之前,这里需要使用 java 存储,我必须用 C 编写一个客户端,它能够从该存储中添加和检索对象)。
I made the program and tried to add 100000 object of size 1KB. But after adding only 50000 objects I am getting 'out of memory' messages (please note that I am printing these 'out of memory' messages whenever I am unable to allocate a new string or byte array using NewStringUTF and NewByteArray functions). At that time my application is using only 80MB of memory. I dont why these methods are returning NULL. Is there something I am missing.
我制作了程序并尝试添加 100000 个大小为 1KB 的对象。但是在仅添加 50000 个对象后,我收到“内存不足”消息(请注意,每当我无法使用 NewStringUTF 和 NewByteArray 函数分配新字符串或字节数组时,我都会打印这些“内存不足”消息)。当时我的应用程序只使用了 80MB 的内存。我不知道为什么这些方法返回 NULL。有什么我想念的吗。
Furthermore, the memory keeps on increasing even though I am releasing byte array and string created for java.
此外,即使我正在释放为 java 创建的字节数组和字符串,内存也会不断增加。
Here is the source code.
这是源代码。
void create_jvm(void)
{
JavaVMInitArgs vm_args;
JavaVMOption vm_options;
vm_options.optionString = "-Djava.class.path=c:\Store";
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 1;
vm_args.options = &vm_options;
vm_args.ignoreUnrecognized = 0;
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(env != null)
{
j_store = (*env)->FindClass(env, "com/store");
if(j_store == null)
{
printf("unable to find class. class name: JStore");
}
}
}
void add(char* key, char* value, int length)
{
jstring j_key = (*env)->NewStringUTF(env, key);
jbyteArray j_value = (*env)->NewByteArray(env, length);
(*env)->SetByteArrayRegion(env, j_value, 0, length, (jbyte *)value);
ret = (*env)->CallStaticBooleanMethod(env, j_store, method_id, j_key, j_value);
if(j_value != null)
{
(*env)->ReleaseByteArrayElements(env, j_value, (jbyte *)value, 0);
}
if(j_key != null)
{
(*env)->ReleaseStringUTFChars(env, j_key, key);
}
}
The java side recieves the data in byte[] and stores it in a hashtable. The issue is that everytime the code runs the memory only adds up and is never released. I tried to add 1 MB object and debugged it.
java 端接收 byte[] 中的数据并将其存储在哈希表中。问题是每次代码运行时,内存只会加起来,永远不会释放。我尝试添加 1 MB 对象并对其进行调试。
The process memory increases by 1MB when I call NewByteArray. But when CallStaticBooleanMethod is called the process memory increase by 4MB. And the call to ReleaseByteArrayElements do not release any memory at all.
当我调用 NewByteArray 时,进程内存增加了 1MB。但是当调用 CallStaticBooleanMethod 时,进程内存增加了 4MB。并且对 ReleaseByteArrayElements 的调用根本不会释放任何内存。
If I add another 1MB object after this, then process memory remains same when I call NewByteArray and it increase by 1MB when I call CallStaticBooleanMethod but remains the same when I try to release the byte array.
如果我在此之后添加另一个 1MB 对象,那么当我调用 NewByteArray 时进程内存保持不变,当我调用 CallStaticBooleanMethod 时它增加 1MB,但在我尝试释放字节数组时保持不变。
回答by Xeor
When you call New... functions, you create a "local reference" - reference to this object in local stack frame. This prevents Java VM from GC this object while you still need it. This is fine if you are implementing some native method - its local frame is created only for method call duration. But when you are creating object from a native java-attached thread, it becomes bound to this thread stack frame, which will be destroyed only with this thread.
当你调用 New... 函数时,你创建了一个“本地引用”——在本地堆栈帧中引用这个对象。这可以防止 Java VM 在您仍然需要此对象时对其进行 GC。如果您正在实现一些本机方法,这很好 - 它的本地框架仅在方法调用持续时间内创建。但是,当您从本地 java 附加线程创建对象时,它会绑定到此线程堆栈帧,只有此线程才会销毁该堆栈帧。
So, when you done with an object you can call DeleteLocalRef() to tell you no longer need it. Or you can surround the whole add() function with pair of PushLocalFrame()/PopLocalFrame() to make a separate local frame for its duration.
因此,当您处理完一个对象后,您可以调用 DeleteLocalRef() 来告诉您不再需要它。或者,您可以使用一对 PushLocalFrame()/PopLocalFrame() 包围整个 add() 函数,以在其持续时间内创建一个单独的本地帧。
回答by Maurice Perry
The purpose of the functions ReleaseByteArrayElements and ReleaseStringUTFChars is not to delete the object but to unlock it after a pointer has been obtained with GetByteArrayElements or GetStringUTFChars. The two if statements should be removed.
函数 ReleaseByteArrayElements 和 ReleaseStringUTFChars 的目的不是删除对象,而是在使用 GetByteArrayElements 或 GetStringUTFChars 获得指针后解锁它。应该删除两个 if 语句。
回答by Walter
Yes, I met the same issue.
是的,我遇到了同样的问题。
My java application call a C++ application by JNI, the C++ application will start a new thread, and call back a java method. In the new thread, many objects were created and the memory increase quickly, though I used DeleteLocalRef, PushLocalFrame and PopLocalFram.
我的java应用程序通过JNI调用C++应用程序,C++应用程序将启动一个新线程,并回调一个java方法。在新线程中,虽然我使用了DeleteLocalRef、PushLocalFrame和PopLocalFram,但创建了许多对象并且内存增加很快。
I found many objects that created by NewObject method cannot be released. It's strange .
我发现很多由 NewObject 方法创建的对象无法释放。真奇怪 。
回答by summer
I tried, all of you said.
我试过了,你们都说。
one must use DeleteLocalRef method after any jstring was created and no more use.
在创建任何 jstring 并且不再使用之后,必须使用 DeleteLocalRef 方法。
Newxxx or CallStaticObjectMethod may create jstring,all of it must delete.
Newxxx 或 CallStaticObjectMethod 可能会创建 jstring,都必须删除。

