Java 和 C 之间某处的内存泄漏
Memory leak somewhere between Java and C
我有一个 java 应用程序,它使用 C 库从相机获取帧。我正在做的是将帧数据存储在 c 库中并将元数据传递给 Java。然后从 Java 它通过 JNI 调用该相机的最新帧数据。
我遇到的问题是,在其中一个函数中,我似乎有内存泄漏。处理开始后,它会在几秒钟内飙升至数 GB。但是,如果我 运行 将 C 库独立地 运行 (基本上是完全相同的代码,只是没有 JNI 组件),则没有内存泄漏。
这是两个 C 函数:
JNIEXPORT jbyteArray JNICALL
Java_com_c_C_1Wrapper_nGet_1Image (JNIEnv *env, jobject jobj, jint i_) {
_jobj = jobj;
jint rs = (*env)->GetJavaVM(env, &jvm);
assert (rs == JNI_OK);
jbyteArray img_data;
int cam_id = i_;
if (!frame_data[cam_id].lock) {
frame_data[cam_id].lock = TRUE;
img_data = (*env)->NewByteArray(env, frame_data[cam_id].data_size);
(*env)->SetByteArrayRegion(env, img_data, 0,
frame_data[cam_id].data_size,
(jbyte*)frame_data[cam_id].img);
frame_data[cam_id].lock = FALSE;
}
return img_data;
}
void
send_frame_data(guint camera_id, gint data_size, void *new_frame,
gint width, gint height, const char *meta_data) {
JNIEnv *env;
jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
assert (rs == JNI_OK);
if (!frame_data[camera_id].lock) {
frame_data[camera_id].lock = TRUE;
free(frame_data[camera_id].img);
frame_data[camera_id].img = malloc(data_size);
memcpy(frame_data[camera_id].img, new_frame, data_size);
frame_data[camera_id].width = width;
frame_data[camera_id].height = height;
frame_data[camera_id].data_size = data_size;
frame_data[camera_id].lock = FALSE;
}
jbyteArray jMetaData = (*env)->NewByteArray(env, strlen(meta_data));
(*env)->SetByteArrayRegion(env, jMetaData, 0, strlen(meta_data), meta_data);
(*env)->CallStaticVoidMethod(env, jSentryCore_class, jImagePreview, jMetaData);
(*env)->DeleteLocalRef(env, jMetaData);
}
需要注意的一点是,*new_frame 对象已从调用方法中释放出来。
Java这边很简单:
@Override
public void ImagePreview(String metaData) {
// parse metaData
byte[] image = C_Wrapper.Get_Image(cameraId);
// do something with data
}
我已经多次重新安排逻辑,但都没有成功。无论我做什么,只要 JNI 部分在运行,就会出现严重的内存泄漏。
这不是内存泄漏,您只是分配内存的速度快于 Java 垃圾收集器释放它的速度。假设1080p@60带16bpp,那就是250MB/s。 C 可以处理它,因为如果大小相等,malloc
很可能会还给您刚刚 free
d 的缓冲区。
您应该停止在每次调用 Get_Image
时调用 NewByteArray
。相反,保留一个固定大小的 byte[]
对象池并更改 Get_Image
以从池中获取缓冲区。当您的 Java 代码完成后,您还需要 return 将缓冲区添加到池中。根据您的 Java 代码对图像的处理方式,您还可以使用直接 ByteBuffer
s 进行调查:您可以直接从 C 写入这些而不是调用 memcpy
.
这会将您的内存顶线限制在您的缓冲区数量之内。请记住,当您调用 Get_Image 并找出处理它的策略时,当前没有可用缓冲区的可能性。
我有一个 java 应用程序,它使用 C 库从相机获取帧。我正在做的是将帧数据存储在 c 库中并将元数据传递给 Java。然后从 Java 它通过 JNI 调用该相机的最新帧数据。
我遇到的问题是,在其中一个函数中,我似乎有内存泄漏。处理开始后,它会在几秒钟内飙升至数 GB。但是,如果我 运行 将 C 库独立地 运行 (基本上是完全相同的代码,只是没有 JNI 组件),则没有内存泄漏。
这是两个 C 函数:
JNIEXPORT jbyteArray JNICALL
Java_com_c_C_1Wrapper_nGet_1Image (JNIEnv *env, jobject jobj, jint i_) {
_jobj = jobj;
jint rs = (*env)->GetJavaVM(env, &jvm);
assert (rs == JNI_OK);
jbyteArray img_data;
int cam_id = i_;
if (!frame_data[cam_id].lock) {
frame_data[cam_id].lock = TRUE;
img_data = (*env)->NewByteArray(env, frame_data[cam_id].data_size);
(*env)->SetByteArrayRegion(env, img_data, 0,
frame_data[cam_id].data_size,
(jbyte*)frame_data[cam_id].img);
frame_data[cam_id].lock = FALSE;
}
return img_data;
}
void
send_frame_data(guint camera_id, gint data_size, void *new_frame,
gint width, gint height, const char *meta_data) {
JNIEnv *env;
jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
assert (rs == JNI_OK);
if (!frame_data[camera_id].lock) {
frame_data[camera_id].lock = TRUE;
free(frame_data[camera_id].img);
frame_data[camera_id].img = malloc(data_size);
memcpy(frame_data[camera_id].img, new_frame, data_size);
frame_data[camera_id].width = width;
frame_data[camera_id].height = height;
frame_data[camera_id].data_size = data_size;
frame_data[camera_id].lock = FALSE;
}
jbyteArray jMetaData = (*env)->NewByteArray(env, strlen(meta_data));
(*env)->SetByteArrayRegion(env, jMetaData, 0, strlen(meta_data), meta_data);
(*env)->CallStaticVoidMethod(env, jSentryCore_class, jImagePreview, jMetaData);
(*env)->DeleteLocalRef(env, jMetaData);
}
需要注意的一点是,*new_frame 对象已从调用方法中释放出来。
Java这边很简单:
@Override
public void ImagePreview(String metaData) {
// parse metaData
byte[] image = C_Wrapper.Get_Image(cameraId);
// do something with data
}
我已经多次重新安排逻辑,但都没有成功。无论我做什么,只要 JNI 部分在运行,就会出现严重的内存泄漏。
这不是内存泄漏,您只是分配内存的速度快于 Java 垃圾收集器释放它的速度。假设1080p@60带16bpp,那就是250MB/s。 C 可以处理它,因为如果大小相等,malloc
很可能会还给您刚刚 free
d 的缓冲区。
您应该停止在每次调用 Get_Image
时调用 NewByteArray
。相反,保留一个固定大小的 byte[]
对象池并更改 Get_Image
以从池中获取缓冲区。当您的 Java 代码完成后,您还需要 return 将缓冲区添加到池中。根据您的 Java 代码对图像的处理方式,您还可以使用直接 ByteBuffer
s 进行调查:您可以直接从 C 写入这些而不是调用 memcpy
.
这会将您的内存顶线限制在您的缓冲区数量之内。请记住,当您调用 Get_Image 并找出处理它的策略时,当前没有可用缓冲区的可能性。