Per*_*-lk 2 c++ java java-native-interface
重要的提示:这段代码不是native从 Java 调用的函数。我们的流程是用 C++ 编写的,我们实例化一个 Java VM,并从 C++ 调用 Java 函数,但绝不会反过来:Java 方法从不调用本机函数,但本机函数会实例化 Java 对象并在其上调用 Java 函数。
我们正在做这样的事情:
void some_fun()
{
// env is a JNIEnv*, cls (and cls2) a jclass, and init (and mid) a jmethodID
jobject obj = env->NewObject(cls, init);
fill_obj(obj, cpp_data);
env->callStaticVoidMethod(cls2, mid, obj);
// env->DeleteLocalRef(obj); // Added out of desperation.
}
Run Code Online (Sandbox Code Playgroud)
其中取决于必须设置为 的字段fill_obj的类型。例如,如果 Java 类包含,将包含,例如,. 所以重载将如下所示:cpp_dataobjclsArrayListcpp_datastd::vectorfill_obj
void fill_obj(jobject obj, SomeType const& cpp_data)
{
std::vector<SomeSubType> const& v = cpp_data.inner_data;
jobject list_obj = env->NewObject(array_list_class_global_ref, its_init_method);
for (auto it = v.begin(); it != v.end(); ++it) {
jobject child_obj = env->NewObject(SomeSubType_class_global_ref, its_init_method);
fill_obj(child_obj , *it);
env->CallBooleanMethod(list_obj, add_method, child_obj);
}
env->SetObjectField(obj, field_id, list_obj);
}
Run Code Online (Sandbox Code Playgroud)
根据我们对 JNI 文档的理解,该代码不应该有任何内存泄漏,因为当本机方法结束时本地对象会被销毁。因此,当第一个fill_obj结束时,在 C++ 端,不再有对其list_obj或其任何子级的引用,并且当some_fun结束时,对obj也消失了。
我的理解是jobject包含某种引用计数器,当该引用计数器达到 0 时,C++ 端就不再引用 Java 对象。如果Java端也不再有对该对象的引用,那么Java的垃圾收集器就可以释放该对象占用的资源。
我们有一个方法,在调用时会创建数千个这样的对象,并且每次调用该方法时,进程在 RAM 中占用的内存(常驻内存)都会增加超过 200 MiB,并且该内存永远不会被释放。
我们添加了一个显式调用DeleteLocalRef但结果是相同的。
到底是怎么回事?我们做错了什么吗?
既然你说你的some_fun()函数不是由Java代码调用的,它只是在内部调用JNI的纯C++代码,那么是的,你将需要调用DeleteLocalRef()你持有的不传回的Java对象的任何本地引用到爪哇。您还需要在for循环内部执行此操作。
尝试这个:
void some_fun()
{
// env is a JNIEnv*, cls (and cls2) a jclass, and init (and mid) a jmethodID
jobject obj = env->NewObject(cls, init); // <-- local ref created
fill_obj(obj, cpp_data);
env->callStaticVoidMethod(cls2, mid, obj);
env->DeleteLocalRef(obj); // <-- local ref released
}
void fill_obj(jobject obj, SomeType const& cpp_data)
{
std::vector<SomeSubType> const& v = cpp_data.inner_data;
jobject list_obj = env->NewObject(array_list_class_global_ref, its_init_method); // <-- local ref created
for (auto it = v.begin(); it != v.end(); ++it) {
jobject child_obj = env->NewObject(SomeSubType_class_global_ref, its_init_method); // <-- local ref created
fill_obj(child_obj, *it);
env->CallBooleanMethod(list_obj, add_method, child_obj);
env->DeleteLocalRef(child_obj); // <-- local ref released
}
env->SetObjectField(obj, field_id, list_obj);
env->DeleteLocalRef(list_obj); // <-- local ref released
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以(Push|Pop)LocalFrame()改为使用,以便 JNI 可以跟踪您创建的所有本地引用,然后一次性为您释放它们,例如:
void some_fun()
{
// env is a JNIEnv*, cls (and cls2) a jclass, and init (and mid) a jmethodID
env->PushLocalFrame(1);
jobject obj = env->NewObject(cls, init);
fill_obj(obj, cpp_data);
env->callStaticVoidMethod(cls2, mid, obj);
env->PopLocalFrame(NULL);
}
void fill_obj(jobject obj, SomeType const& cpp_data)
{
std::vector<SomeSubType> const& v = cpp_data.inner_data;
env->PushLocalFrame(1+v.size());
jobject list_obj = env->NewObject(array_list_class_global_ref, its_init_method);
for (auto it = v.begin(); it != v.end(); ++it) {
jobject child_obj = env->NewObject(SomeSubType_class_global_ref, its_init_method);
fill_obj(child_obj, *it);
env->CallBooleanMethod(list_obj, add_method, child_obj);
}
env->SetObjectField(obj, field_id, list_obj);
env->PopLocalFrame(NULL);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
891 次 |
| 最近记录: |