java - JNI 代码中的内存泄漏

标签 java c java-native-interface ipc

下面是在 C 和 Java 进程之间桥接 Linux MQ 的 JNI 代码。虽然我已经释放了所有的ArrayElements,但是top命令的VIRT仍然显示出巨大的值(value)。最大堆大小设置为 2GB,但执行 100 小时后顶部显示 VIRT 为 10GB。对我来说,这看起来像是内存泄漏,但是,我无法弄清楚 JNI 代码的哪一部分导致了问题。如果有人能在这方面帮助我,那就太好了。谢谢。

我的JDK版本是1.8.0_91

这是我写的mq_receive方法

JNIEXPORT int JNICALL Java_test_ipc_impl_LinuxMessageQueue_mq_1receive(
        JNIEnv *env, jobject self, jint mqdes, jbyteArray buffer, jint msglen) {
    jbyte* buf = (*env)->GetByteArrayElements(env, buffer, NULL);
    if ((*env)->ExceptionCheck(env))
        return -1;

    struct timespec timeout;

    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 1;

    int size = mq_timedreceive(mqdes, (char*) buf, msglen, 0, &timeout);

    if (size == -1) {
        if (errno == ETIMEDOUT) {
            size = 0;
        } else {
            perror("mq_receive fail");
        }
    } else {
        (*env)->SetByteArrayRegion(env, buffer, 0, size, buf);
        if ((*env)->ExceptionCheck(env))
            return -1;
    }

    (*env)->ReleaseByteArrayElements(env, buffer, buf, JNI_COMMIT);
    if ((*env)->ExceptionCheck(env))
        return -1;

    return size;
}

而且,这是我写的 mq_send 方法

JNIEXPORT void JNICALL Java_test_ipc_impl_LinuxMessageQueue_mq_1send(
        JNIEnv *env, jobject self, jint mqdes, jbyteArray buffer, jint msglen) {
    jbyte* buf = (*env)->GetByteArrayElements(env, buffer, NULL);
    if ((*env)->ExceptionCheck(env))
        return;

    if (mq_send(mqdes, (char*) buf, msglen, 0) == -1) {
        perror("mq_send fail");
    }
    (*env)->ReleaseByteArrayElements(env, buffer, buf, JNI_COMMIT);
    if ((*env)->ExceptionCheck(env))
        return;

}

最佳答案

Here's the meaning of the flags您传递给 ReleaseByteArrayElements 作为最后一个参数:

The possible settings of the mode flag are:

0 Update the data on the Java heap. Free the space used by the copy.

JNI_COMMIT Update the data on the Java heap. Do not free the space used by the copy.

JNI_ABORT Do not update the data on the Java heap. Free the space used by the copy.

The ‘0' mode flag is the safest choice for the Release call. Whether the copy of the data was changed or not, the heap is updated with the copy, and there are no leaks.

因此,在您的 mq_receive 函数中,调用 ReleaseByteArrayElements 并传递 0 作为最终参数。您不需要 SetByteArrayRegion 调用,因为数据将由 ReleaseByteArrayElements 复制回来。

在您的 mq_send 函数中,您可以传递 JNI_ABORT,因为您没有写入数组。

这应该在两种情况下释放缓冲区。

上面假设缓冲区是副本而不是固定引用。但我认为它是一个副本,否则就不会泄漏。您可以通过将 &isCopy 参数传递给 GetByteArrayElements 来找到答案。

关于java - JNI 代码中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43650572/

相关文章:

java - 如何从 Android 中的方法返回 Firebase 中子项的值?

c++ - C++ 中独立于操作系统的线程?

android - Mupdf 仅支持 pdf 编译

c++ - eclipse 可以使用像 cocos2d 这样的外部库来自动完成吗?

java - 设置 java.library.path 的替代方法?

java - spring org.springframework.core.io.UrlResource不支持https资源

java - 对象和抽象的ArrayList

java - 使用 Java/Selenium 从网站下载视频?

c - 程序不接受 EOF 以停止读取标准输入

自定义系统调用可以访问另一个进程的内存吗?