JNI 如何管理 C++ 缓冲区的生命周期

JNI how to manage the life cycle of a c++ buffer

我在我的 C++ 代码中的堆上分配了一个图像缓冲区,我想通过 JNI 与其他一些 C++ 对象以及 Java 对象共享。在本机方面,我使用 shared_ptr,我想知道最好的方法是什么?我的想法是在堆上分配一次缓冲区并在所有地方共享一个引用。我正在利用智能指针,以便在所有引用超出范围后立即释放缓冲区,但在共享对 java 端的引用时我遇到了问题。

如何确保我的 java 对象始终具有对缓冲区的有效引用?当 java 对象使用其引用完成时,c++ 如何确定引用计数器达到 0。我关心的是避免内存泄漏,并确保缓冲区在被 java class.

处理之前不会被过早销毁

感谢帮助

一般答案是"make the Java object's lifetime influence the lifetime of the C++ object"。

从以下内容开始 Java class:

class Refholder implements AutoCloseable {
    private long ptr; // the actual pointer
    private long shared_ptr; // a pointer to a shared_ptr keeping `ptr` alive
    public Refholder(long ptr, long shared_ptr) {
        this.ptr = ptr;
        this.shared_ptr = shared_ptr;
    }
    public native void close();
    public void finalize() { close(); }
    // Other methods to access the contents of `ptr` go here.
};

这将包含实际指针和指向 shared_ptr 的指针。

如果您想提交对 Java 的引用,请使用以下内容:

jobject passToJava(JNIEnv *env, std::shared_ptr<Foo> instance) {
    jclass cls_Refholder = env->FindClass("Refholder");
    jmethodID ctr_Refholder = env->GetMethodID(cls_Refholder, "<init>", "(JJ)V");
    // This line increases the reference count and remembers where we put the copy
    std::shared_ptr<Foo> *copy = new std::shared_ptr<Foo>(std::move(instance));
    jobject ret = env->NewObject(cls_Refholder, ctr_Refholder, copy->get(), copy);
    return ret;
}

最后,close方法负责提取shared_ptr并释放它:

JNIEXPORT void Java_Refholder_close(JNIEnv *env, jobject obj) {
    jclass cls_Refholder = env->GetObjectClass(obj);
    jfieldID fld_Refholder_ptr = env->GetFieldID(cls_Refholder, "ptr", "J");
    jfieldID fld_Refholder_shared_ptr = env->GetFieldID(cls_Refholder, "shared_ptr", "J");

    std::shared_ptr<Foo> *copy = (std::shared_ptr<Foo>*)env->GetLongField(obj, fld_Refholder_shared_ptr);
    if (!copy)
        return;

    env->SetLongField(obj, fld_Refholder_ptr, 0);
    env->SetLongField(obj, fld_Refholder_shared_ptr, 0);
    delete copy;
}

我决定同时实现 AutoCloseablefinalize,因为我不知道您的 Java 代码计划如何使用引用。如果您需要对 C++ 对象进行确定性破坏,则需要使用 try-with-resources 或显式调用 close 方法。如果你不这样做,最后 finalize 将关闭它。