下面是在 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/