使用jni从c ++调用java时出现内存泄漏

时间:2018-04-10 09:33:47

标签: java c++ java-native-interface

我有一个Java-Application,它合并了一些大文件。 Java应用程序不在我的控制之下。 Java-Application的结果作为一个合适的90 Mb大字符串返回到我的C ++程序,我在一些算法中使用它。我多次调用execute-mathod。我的问题是,每次我调用Java-Application时它都会保留更多内存,但不会释放它。从这个Garbage collection and JNI call问题我有想法手动调用垃圾收集器但它没有释放内存。 有什么想法解决这个问题吗?

这是我的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);
}

2 个答案:

答案 0 :(得分:0)

您的记忆处理存在一些问题:

  1. void JavaWrapperClass::CreateVM(string Classpath)函数中,您在new[]变量上使用options运算符,但之后使用delete删除它。这是UB。您必须使用delete[]正确删除此指针。
  2. const string* JavaWrapperClass::Execute(const string& Filename, const string& HV, const string& NV, const string& FileId)函数中,您将返回指向名为mergedFilesStr的字符串的指针。在你的函数伙伴中,你正在分配这个指针,然后返回它,所以你在使用它之后需要delete它(是吗?)。我可以看到你在函数的开头使用了erase()shrink_to_fit(),但要注意这个函数与删除分配的指针无关,所以你需要将它自己删除为:{{ 1}}
  3. 无论如何更好的方法是使用smart pointers。你可以通过使用它们来避免这种手动内存管理问题,它们很棒。

答案 1 :(得分:0)

无论isCopy是否为真,您都应该致电ReleaseStringUTFCharsShould you call ReleaseStringUTFChars if GetStringUTFChars returned a copy?

我不知道mergedFilesStr如何被decalred / used,但它也可能是你泄漏的来源。您的示例似乎在::Execute的开头使用它作为对象,但之后作为指针使用它?事实上,我不知道发布的代码是如何编译的。