【问题标题】:Memory leak in JNI codeJNI 代码中的内存泄漏
【发布时间】:2017-04-27 06:52:11
【问题描述】:

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

}

【问题讨论】:

    标签: java c java-native-interface ipc


    【解决方案1】:

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

    模式标志的可能设置是:

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

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

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

    “0”模式标志是 Release 调用最安全的选择。无论数据的副本是否发生变化,堆都随着副本更新,并且没有泄漏。

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

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

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

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

    【讨论】:

    • 非常感谢。会试试看tmr
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多