使用JNI从C调用Java代码时内存泄漏

时间:2009-08-27 13:01:16

标签: java c memory-leaks java-native-interface

我有一个C程序,它使用JNI在Java商店中存储一些对象。 (在有人问之前,使用java商店是一个需求,我必须在C中编写一个能够从该商店添加和检索对象的客户端。)

我制作了程序并尝试添加100000大小为1KB的对象。但是在添加了50000个对象后,我收到了“内存不足”的消息(请注意,每当我无法使用NewStringUTF和NewByteArray函数分配新的字符串或字节数组时,我就会打印这些“内存不足”消息)。那时我的应用程序只使用80MB的内存。我不知道为什么这些方法返回NULL。有什么我想念的。

此外,即使我正在发布为java创建的字节数组和字符串,内存也在不断增加。

这是源代码。

    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);
    }
}

java端接收byte []中的数据并将其存储在哈希表中。 问题是,每次代码运行时,内存只会累加而且永远不会释放。 我试图添加1 MB对象并进行调试。

当我调用NewByteArray时,进程内存增加1MB。但是当调用CallStaticBooleanMethod时,进程内存增加4MB。对ReleaseByteArrayElements的调用根本不会释放任何内存。

如果我在此之后添加另一个1MB对象,那么当我调用NewByteArray时进程内存保持不变,当我调用CallStaticBooleanMethod时它增加1MB但在我尝试释放字节数组时保持相同。

4 个答案:

答案 0 :(得分:17)

当您调用New ...函数时,您将创建一个“本地引用” - 在本地堆栈框架中引用此对象。这样可以在您仍然需要时阻止Java VM来自GC。如果要实现一些本机方法,这很好 - 它的本地帧仅为方法调用持续时间创建。但是,当您从本机java连接线程创建对象时,它将绑定到此线程堆栈帧,该帧将仅使用此线程销毁。

因此,当您完成对象时,可以调用DeleteLocalRef()来告诉您不再需要它。或者你可以用一对PushLocalFrame()/ PopLocalFrame()包围整个add()函数,在其持续时间内创建一个单独的本地帧。

答案 1 :(得分:4)

函数ReleaseByteArrayElements和ReleaseStringUTFChars的目的不是删除对象,而是在使用GetByteArrayElements或GetStringUTFChars获取指针后将其解锁。 应该删除两个if语句。

答案 2 :(得分:0)

是的,我遇到了同样的问题。

我的java应用程序通过JNI调用C ++应用程序,C ++应用程序将启动一个新线程,并回调一个java方法。在新线程中,虽然我使用了DeleteLocalRef,PushLocalFrame和PopLocalFram,但是创建了很多对象并且内存也快速增加。

我发现很多由NewObject方法创建的对象无法释放。真奇怪 。

答案 3 :(得分:0)

我试过了,你们所有人都说。

必须在创建任何jstring之后使用DeleteLocalRef方法,并且不再使用。

Newxxx或CallStaticObjectMethod可能会创建jstring,所有这些都必须删除。