当前位置: 首页 > 知识库问答 >
问题:

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

曾新立
2023-03-14

我有一个Java应用程序,它合并了一些大文件。Java应用程序不在我的控制之下。Java应用程序的结果以大约90MB的大字符串返回给我的C程序,我在其中的一些算法中使用它。我多次调用execute方法。我的问题是,每次调用Java应用程序时,它都会保留更多内存,但不会释放内存。从这个垃圾收集和JNI调用问题中,我想到手动调用垃圾收集器,但它不会释放内存。有没有办法解决这个问题?

这是我的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个答案

穆远
2023-03-14

无论isCopy是否为true,都应始终调用ReleaseStringUTFChars:如果GetStringUTFChars返回副本,是否应调用ReleaseStringUTFChars?。

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

太叔豪
2023-03-14

这里有几个内存处理问题:

  1. void JavaWrapperClass::CreateVM(字符串类路径)函数中,您在options变量上使用new[]运算符,但随后使用delete删除它。这是UB。必须使用delete[]正确删除此指针
  2. 常量字符串*JavaWrapperClass::Execute(常量字符串)中

无论如何,更好的方法是使用智能指针。您可以通过使用它们来避免这种手动内存管理问题,它们非常棒。

 类似资料:
  • 问题内容: 我有一个C程序,它使用JNI在Java存储中存储一些对象。(在有人问之前,这里需要使用Java存储,而我必须用C编写一个客户端,该客户端才能从该存储中添加和检索对象)。 我制作了程序,并尝试添加100000个大小为1KB的对象。但是在仅添加50000个对象之后,我得到了“内存不足”消息(请注意,每当我无法使用NewStringUTF和NewByteArray函数分配新的字符串或字节数组

  • 我正在制作一个库应用程序,该应用程序使用谷歌断路器检测android中的本机崩溃。每当我的主端发生本机崩溃时,断路器都会调用以下回调。从这个回调中,我需要使用JNI在java类中调用一个静态val方法。 这是我的java方法: 在Android5.0之前,这一功能一直运行良好。但在Lollipop中,我无法调用我的java方法,因为我无法在Logcat控制台上看到“内部句柄异常”日志。 以下是我在

  • 我用java写了一个项目,使用JNI使用C++库。所有的代码都是我们写的,所以我有所有的源代码。 几个小时后,机器内存耗尽,尽管我的进程只是迭代文件,并且删除了与上一个文件有关的所有内存。 谢了。

  • 问题内容: 当我使用JNI方法构建Java对象时,为了将其作为参数传递给我使用JNI调用API进行调用的Java方法,如何管理它的内存? 这是我正在处理的内容: 我有一个C对象,该对象的析构函数要复杂得多。这个C对象将与Java对象相关联,一旦应用程序完成了Java对象,我将不再需要C对象。 我正在这样创建Java对象(为了清楚起见,省略了错误检查): 那么,既然我已经完成了,该怎么办?理想情况下

  • 问题内容: 所以我有这个C ++程序,它是通过Java程序中的JNI调用的,代码如下: 在倒数第二行中,从不释放而是返回,是否会导致最终的内存泄漏?反正有解决这个问题的方法吗? 还有可能不是返回字符串而是返回布尔值(由LogonUser函数返回),而不是jstring,而是添加了要在方法中传递的“ errormessage”引用,并更新了它?我的Java程序能否看到“ errormessage”的

  • 问题内容: 我尝试在Android NDK上工作,我的第一个测试不是很确定,我需要帮助,因为我看不到错误在哪里。 以下代码可以毫无问题地进行编译,但是当在模拟器上运行时,该程序将返回SIGSEGV信号,并且在logcat中不会显式写入任何错误。但是,将显示一条警告,指出未找到Java类。经过一天的研究,这个问题看起来一切正确。 这是我的Java代码:JNITestActivity.java 这是我