当前位置: 首页 > 面试题库 >

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

孙成益
2023-03-14
问题内容

我有一个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,但在尝试释放字节数组时将保持不变。


问题答案:

调用New …函数时,将创建一个“本地引用”-在本地堆栈框架中对此对象的引用。这样可以防止Java
VM在仍需要该对象时从其GC。如果您要实现某些本机方法,这很好-
它的局部框架仅在方法调用期间创建。但是,当您从与Java关联的本机线程创建对象时,该对象将绑定到该线程堆栈框架,该框架仅会被该线程破坏。

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



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

  • 问题内容: 虽然我可以使用Java代码的Eclipse JDT调试器和C代码的GDB调试应用程序,但我还是希望使用单个工具进行所有调试。我找到了几个在Eclipse中启用“混合模式”调试的项目,其中包括对Java和本机代码之间的单步执行的支持。 英特尔针对Java / JNI环境的集成调试器 Mariot Chauvin的“代码之夏”项目:支持JDT和CDT之间的无缝调试 不幸的是,其中一个声称是

  • 问题内容: 有没有人能将C#代码集成到Java应用程序中呢? 代码很小,所以我可以用Java重写,但是如果可能的话,我宁愿重用代码。不要重复自己,等等。 另外,我知道我可以将C#公开为Web服务或其他任何东西,但是其中包含一些安全性/加密功能,因此,我宁愿将其紧密集成。 编辑:它将在基于服务器的应用程序上,因此“下载”另一个运行时是无关紧要的。 问题答案: 有一个IL到Java字节码编译器Gras

  • gcc-g-i/usr/lib/jvm/java-1.6.0-openJDK-1.6.0.0.x86_64/include/-i/usr/lib/jvm/java-1.6.0-openJDK-1.6.0.0.x86_64/include/-i/usr/lib/jvm/java-1.6.0-openJDK-1.6.0.0.x86_64/include/-i/usr/lib/jvm/java-1.6.

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

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