JNI代码中的内存泄漏

时间:2017-04-27 06:52:11

标签: java c java-native-interface ipc

以下是在C和Java进程之间桥接Linux MQ的JNI代码。虽然我已经发布了所有的ArrayElements,但顶级命令的VIRT仍显示出巨大的价值。最大堆大小设置为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;

}

1 个答案:

答案 0 :(得分:2)

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

  

模式标志的可能设置为:

     

0 更新Java堆上的数据。释放副本使用的空间。

     

<强> JNI_COMMIT   更新Java堆上的数据。不要释放副本使用的空间。

     

<强> JNI_ABORT   不要更新Java堆上的数据。释放副本使用的空间。

     

'0&#39; mode flag是Release调用的最安全选择。无论是否更改了数据副本,都会使用副本更新堆,并且没有泄漏。

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

mq_send功能中,您可以通过JNI_ABORT,因为您没有写入数组。

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

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