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 很可能会还给您刚刚 freed 的缓冲区。

您应该停止在每次调用 Get_Image 时调用 NewByteArray。相反,保留一个固定大小的 byte[] 对象池并更改 Get_Image 以从池中获取缓冲区。当您的 Java 代码完成后,您还需要 return 将缓冲区添加到池中。根据您的 Java 代码对图像的处理方式,您还可以使用直接 ByteBuffers 进行调查:您可以直接从 C 写入这些而不是调用 memcpy.

这会将您的内存顶线限制在您的缓冲区数量之内。请记住,当您调用 Get_Image 并找出处理它的策略时,当前没有可用缓冲区的可能性。