简体   繁体   English

使用jni从c ++调用Java时发生内存泄漏

[英]memory leak while calling java from c++ with jni

I have a Java-Application, that merges some large files. 我有一个Java应用程序,它合并了一些大文件。 The Java-Application isn't under my control. Java应用程序不受我的控制。 The result from the Java-Applicationis returned as a approimatly 90 Mb large string to my C++ Program where I use it in some algorythms. Java应用程序的结果以大约90 Mb的大字符串形式返回给我的C ++程序,在某些算法中我会在其中使用它。 I call the execute-mathod several times. 我多次打电话给execute-mathod。 My problem is, every time I call the Java-Application it reserves more memory but doesnt free it. 我的问题是,每次调用Java应用程序时,它都会保留更多的内存,但不会释放它。 From this Garbage collection and JNI call question I had the idea to call the garbage collector manually but it frees no memory. 在这个垃圾回收和JNI调用问题中,我想到了手动调用垃圾回收器的想法,但它没有释放内存。 Any idea to fix that problem? 有解决这个问题的主意吗?

Here is my C++-Program 这是我的C ++程序

void JavaWrapperClass::CreateVM(string Classpath)
{
        Classpath.insert(0,"-Djava.class.path=");
                             // Pointer to native interface
        //================== prepare loading of Java VM ============================
        JavaVMInitArgs vm_args;                        // Initialization arguments
        JavaVMOption* options = new JavaVMOption[2];   // JVM invocation options

        options[0].optionString =const_cast<char*>(Classpath.c_str()); // where to find java .class
        string maxMemOption=string("-Xmx")+to_string(logicalSolverMaxMem)+"m";
        options[1].optionString=const_cast<char*>(maxMemOption.c_str());

        vm_args.version = JNI_VERSION_1_8;             // minimum Java version
        vm_args.nOptions = 2;                          // number of options
        vm_args.options = options;
        vm_args.ignoreUnrecognized = false;     // invalid options make the JVM init fail

        //=============== load and initialize Java VM and JNI interface =============
        jint rc = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);  // YES !!
        delete options;    // we then no longer need the initialisation options.
        if (rc != JNI_OK)
        {
            throw bad_exception();
        }
}

const string* JavaWrapperClass::Execute(const string& Filename, const string& HV, const string& NV,
        const string& FileId)
{

    mergedFilesStr.erase();
    mergedFilesStr.shrink_to_fit();


    jclass javaClass = env->FindClass("Path_to/My_Class");  // try to find the class
    if (javaClass == nullptr)
    {
        throw JavaWrapper_JNI_runtime_exception("class Path_to/My_Class not initialized!");
    }

    jmethodID ctor = env->GetMethodID(javaClass, "<init>", "()V");  // FIND AN OBJECT CONSTRUCTOR
    if (ctor == nullptr)
    {
        env->DeleteLocalRef(javaClass);
        throw JavaWrapper_JNI_runtime_exception("Constructor not found");
    }

    jobject javaObject;
    javaObject = env->NewObject(javaClass, ctor);
    if (javaObject==nullptr)
    {
        env->DeleteLocalRef(javaClass);
        throw JavaWrapper_JNI_runtime_exception("Could not create Java-Object");
    }

    jmethodID mid =
            env->GetMethodID(javaClass, "execute",
                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); // find method
    if (mid == nullptr)
    {
        env->DeleteLocalRef(javaObject);
        env->DeleteLocalRef(javaClass);
        throw JavaWrapper_JNI_runtime_exception("Method string execute(String odx_Filename, String HV, String NV, String FileId) not found !");
    }
    else
    {
        logger->debug("Found JAVA method execute. => Call execute");
        jstring filename = env->NewStringUTF(Filename.c_str());
        jstring hv = env->NewStringUTF(HV.c_str());
        jstring nv = env->NewStringUTF(NV.c_str());
        jstring FileId = env->NewStringUTF(FileId.c_str());
        jstring retString = (jstring) env->CallObjectMethod(javaObject, 
            mid, filename, hv, nv, FileId);   // call the method "execute" with arguments.

        jboolean isCopy=JNI_TRUE;
        const char *mergedFilesPtr;
        mergedFilesPtr = env->GetStringUTFChars(retString, &isCopy);
        mergedFilesStr= new string(mergedFilesPtr);


        if (isCopy == JNI_TRUE) 
        {
            //Release memory from Return-String
            env->ReleaseStringUTFChars(retString, mergedFilesPtr);
        }
        callGarbageCollector();

        env->DeleteLocalRef(filename);
        env->DeleteLocalRef(hv);
        env->DeleteLocalRef(nv);
        env->DeleteLocalRef(FileId);
    }

    env->DeleteLocalRef(javaObject);
    env->DeleteLocalRef(javaClass);
    callGarbageCollector();

    return &mergedFilesStr;
}

void JavaWrapperClass::callGarbageCollector()
{
    jclass    systemClass    = nullptr;
    jmethodID systemGCMethod = nullptr;

    systemClass    = env->FindClass("java/lang/System");
    systemGCMethod = env->GetStaticMethodID(systemClass, "gc", "()V");
    env->CallStaticVoidMethod(systemClass, systemGCMethod);

    env->DeleteLocalRef(systemClass);
}

There are a couple issues with your memory handling here: 您的内存处理有两个问题:

  1. in void JavaWrapperClass::CreateVM(string Classpath) function, you are using new[] operator on options variable but then you are deleting it using delete . void JavaWrapperClass::CreateVM(string Classpath)函数中,您正在options变量上使用new[]运算符,但随后使用delete删除了它。 This is UB. 这是UB。 you must use delete[] to delete this pointer correctly. 您必须使用delete[]正确删除此指针。
  2. in const string* JavaWrapperClass::Execute(const string& Filename, const string& HV, const string& NV, const string& FileId) function you are returning a pointer to string named mergedFilesStr . const string* JavaWrapperClass::Execute(const string& Filename, const string& HV, const string& NV, const string& FileId)函数中,您正在返回一个指向名为mergedFilesStr字符串的指针。 In your function buddy you are allocating this pointer and then returning it, So you need to delete it after you are done using it (do you?). 在您的函数伙伴中,您正在分配此指针,然后返回它,因此,在使用delete它之后需要将其delete (对吗?)。 I can see you used erase() and shrink_to_fit() in the beginning of your function but beware that this function has nothing to do with deleting your allocated pointer, So you need to delete it your self like this : delete mergedFilesStr; 我可以看到您在函数的开头使用了erase()shrink_to_fit() ,但是请注意,该函数与删除分配的指针无关,因此,您需要像这样删除自己的指针: delete mergedFilesStr;

Anyway the better approach is using smart pointers . 无论如何 ,更好的方法是使用智能指针 you can avoid this kind of manual memory management problems by using them, They are awesome. 您可以通过使用它们来避免这种手动的内存管理问题,它们很棒。

You should always call ReleaseStringUTFChars regardless of whether isCopy is true or not: Should you call ReleaseStringUTFChars if GetStringUTFChars returned a copy? 无论isCopy是否为真,都应该始终调用ReleaseStringUTFChars如果GetStringUTFChars返回了副本 ,是否应该调用ReleaseStringUTFChars? .

I don't know how mergedFilesStr is being decalred/used but it could also be the source of your leak. 我不知道如何对mergedFilesStr进行缩放/使用,但它也可能是泄漏的根源。 Your example seems to use it as an object at the beginning of ::Execute but then as a pointer later on? 您的示例似乎将其用作::Execute开头的对象,但随后用作指针? In fact I don't see how the code as posted even compiles. 实际上,我什至看不到所发布的代码是如何编译的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM