以下是在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;
}
答案 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
来找到答案。