#define PRINTF(...) ((void)__android_log_print(ANDROID_LOG_INFO, "yaui", __VA_ARGS__))
jfindViewById = (Env)->GetMethodID(cls, "findViewById", "(I)Landroid/view/View;");
for (int i = 0; i < 1000; i++) {
PRINTF("%i ", i);
view = (jobject) (Env)->CallObjectMethod(Obj, jfindViewById, N);
}
循环将执行大约500次,然后程序将崩溃。 我无法理解为什么。必须是内存泄漏或资源泄漏,但这可能泄漏到什么地方?
在现实生活中,我不需要像这样一次执行1000次此功能。这是我在搜索问题时创建的最小循环。
答案 0 :(得分:2)
显然,您的findViewById
方法返回Java对象,并且您正在jobject
中存储这些view
引用并始终覆盖它们。但是,这对JVM来说是一个问题:垃圾收集如何知道持有jobject
对象引用的本机代码?我知道一些JavaScript引擎只是遍历整个本机堆栈并检查是否有任何值是有效的对象引用。脏。 JVM使用不同的方法:当线程进入本机代码时,在JVM中创建本地参考框架。每当本机代码被赋予本地jobject
引用时,通过方法参数,通过调用返回对象的方法或通过从字段读取对象值,该引用被添加到该JVM堆栈帧。现在GC可以查看堆栈帧并立即查看正在引用的对象。然后,当本机代码返回时,将删除特殊堆栈帧,因此将释放本地引用。根据最新的JNI文档,JVM默认为该堆栈帧中的 16 本地引用分配空间,但为了保持向后兼容性,它可以分配更多(它可能会调整大小)缓冲或类似的东西)。错误消息表明JVM无法无限地执行此操作(max=512
),并且您的1000个引用(每个循环迭代一个)明显超出该限制。
现在你有几个选择:
DeleteLocalRef
方法将释放本地引用,允许您在引用缓冲区中重用空间。EnsureLocalCapacity
验证当前线程是否可以容纳给定数量的引用。它将返回一个负数,如果不是这样,则抛出OutOfMemoryError
。PushLocalFrame
创建具有您选择的容量的新本地参考框架。完成后,您可以通过拨打PopLocalFrame
一次性释放该框架内的所有引用。如果可以,您当然应该在不再需要本地参考时删除它们。
答案 1 :(得分:0)
看着
int __android_log_print(int prio, const char *tag, const char *fmt, ...);
所以你的#define应该有两个逻辑参数:一个用于格式字符串,其余的是参数:
#define PRINTF(format, ...) \
((void)__android_log_print(ANDROID_LOG_INFO, "yaui", format, __VA_ARGS__))
格式字符串实际上不是varargs的一部分。但从逻辑的角度来看,它用于检测堆栈后面的预期参数的数量。