代码之家  ›  专栏  ›  技术社区  ›  ata

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

  •  7
  • ata  · 技术社区  · 15 年前

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

    我做了这个程序,试图添加100000个1KB大小的对象。但是在只添加50000个对象之后,我会收到“内存不足”的消息(请注意,每当我无法使用newstringutf和new byte array函数分配新的字符串或字节数组时,我都会打印这些“内存不足”的消息)。当时我的应用程序只使用80MB内存。我不知道为什么这些方法返回空值。我有什么东西不见了吗?

    此外,即使我正在释放为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端接收字节[]中的数据并将其存储在哈希表中。 问题是,每次代码运行时,内存只会累积,永远不会释放。 我尝试添加1 MB对象并调试它。

    当我调用newytearray时,进程内存增加了1MB。但是当调用callstaticbooleanmethod时,进程内存会增加4MB。调用releaseByterrayElements根本不会释放任何内存。

    如果在这之后再添加一个1MB对象,那么当我调用newbytearray时,进程内存保持不变,当我调用callstaticBooleanMethod时,进程内存增加了1MB,但当我尝试释放字节数组时,进程内存保持不变。

    4 回复  |  直到 11 年前
        1
  •  17
  •   Xeor    15 年前

    当你叫新的…函数中,创建一个“本地引用”——在本地堆栈框架中引用该对象。这样就可以防止Java VM在您需要的时候从这个对象中获取GC。如果您正在实现某个本机方法,这很好——它的本地框架只为方法调用持续时间创建。但是,当您从原生Java附加线程创建对象时,它会绑定到这个线程堆栈框架,该线程堆栈框架将仅用该线程销毁。

    所以,当您处理完一个对象后,您可以调用deleteLocalRef()来告诉您不再需要它。或者,您可以用pushlocalframe()/poplocalframe()对整个add()函数进行包围,以在其持续时间内形成单独的本地帧。

        2
  •  4
  •   Maurice Perry    15 年前

    函数releaseBytearrayElements和releaseStringutfChars的目的不是删除对象,而是在使用getBytearrayElements或getStringutfChars获取指针后将其解锁。 应删除这两个if语句。

        3
  •  0
  •   j0k gauthamp    12 年前

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

    我的Java应用程序通过JNI调用C++应用程序,C++应用程序将启动一个新线程,并调用一个Java方法。在新的线程中,创建了许多对象,并且内存增长很快,尽管我使用了deleteLocalRef、pushLocalFrame和popLocalFram。

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

        4
  •  0
  •   summer    11 年前

    你们都说,我试过了。

    在创建任何JString之后,必须使用deleteLocalRef方法,不能再使用。

    newxxx或callstaticObjectMethod可能会创建JString,所有的JString都必须删除。