1
#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 次。这是我为寻找问题而创建的最小循环。

4

2 回答 2

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

如果可以,您当然应该在不再需要本地引用时立即删除它们。

于 2013-06-07T15:27:40.097 回答
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__))

格式字符串实际上并不是可变参数的一部分。但从逻辑的角度来看,它用于检测堆栈上的预期参数的数量。

于 2013-06-07T09:10:47.350 回答