多次调用findViewById后Java/JNI突然崩溃 #定义PRINTF(…)((void)uuu android_log_print(android_log_INFO,“yaui”,“VA_ARGS”) jfindViewById=(Env)->GetMethodID(cls,“findViewById”,“I)Landroid/view/view;”; 对于(int i=0;i调用对象方法(Obj,jfindViewById,N); }

多次调用findViewById后Java/JNI突然崩溃 #定义PRINTF(…)((void)uuu android_log_print(android_log_INFO,“yaui”,“VA_ARGS”) jfindViewById=(Env)->GetMethodID(cls,“findViewById”,“I)Landroid/view/view;”; 对于(int i=0;i调用对象方法(Obj,jfindViewById,N); },java,android,java-native-interface,Java,Android,Java Native Interface,循环将执行大约500次,然后程序将崩溃。 我很难理解为什么。一定是内存泄漏或资源泄漏,但这里可能泄漏什么 在现实生活中,我不需要像那样一次执行1000次这个函数。这是我在搜索问题时创建的最小循环。查看 int\uu android\u log\u打印(int-prio,const-char*tag,const-char*fmt,…) 因此#define应该有两个逻辑参数:一个用于格式字符串,其余的是参数: #define PRINTF(...) ((void)__android_log_prin

循环将执行大约500次,然后程序将崩溃。 我很难理解为什么。一定是内存泄漏或资源泄漏,但这里可能泄漏什么

在现实生活中,我不需要像那样一次执行1000次这个函数。这是我在搜索问题时创建的最小循环。

查看

int\uu android\u log\u打印(int-prio,const-char*tag,const-char*fmt,…)

因此#define应该有两个逻辑参数:一个用于格式字符串,其余的是参数:

#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);
}

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

显然,您的
findViewById
方法返回Java对象,您将这些
jobject
引用存储在
view
中并总是覆盖它们。然而,对于JVM来说,这是一个问题:垃圾收集如何知道包含对对象的
jobject
引用的本机代码?我知道一些JavaScript引擎只是遍历整个本机堆栈,并检查是否存在任何有效的对象引用值。肮脏的。JVM使用不同的方法:当线程输入本机代码时,JVM中会创建一个本地参考帧。每当本地代码被给予本地
jobject
引用时,无论是通过方法参数、调用返回对象的方法还是通过从字段中读取对象值,该引用都会添加到JVM堆栈帧中。现在GC只需查看堆栈帧,就可以立即看到引用了哪些对象。当本机代码返回时,特殊堆栈帧被删除,因此本地引用被释放。根据最新的JNI文档,JVM在默认情况下为该堆栈帧中的16本地引用分配空间,但为了保持向后兼容性,它可以分配更多空间(可能会调整缓冲区大小或类似的内容)。错误消息表明JVM不会无限地执行此操作(
max=512
),您的1000个引用(每个循环迭代一个)显然超过了该限制

现在您有几个选项:

  • 删除本地引用:该方法将释放本地引用,允许您重用引用缓冲区中的空间
  • 确保本地引用容量:验证当前线程是否可以容纳给定数量的引用。如果不是这样的话,它将返回一个负数并抛出一个
    OutOfMemoryError
  • 创建一个新的局部参照系:您可以使用创建一个容量由您选择的新局部参照系。完成后,可以通过调用一次释放该框架内的所有引用

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

有日志/堆栈跟踪吗?是的,日志很有用,但我仍然感到困惑W/dalvikvm(19617):可引用溢出(max=512)W/dalvikvm(19617):JNI本地引用表中的最后10个条目:W/dalvikvm(19617):502:0x4051c438 cls=Ltw/ksana/javacpp/javacpp$MYView;(252字节)W/dalvikvm(19617):503:0x4051c438 cls=Ltw/ksana/javacpp/javacpp$MYView;(252字节)W/dalvikvm(19617):504:0x4051c438 cls=Ltw/ksana/javacpp/javacpp$MYView;(252字节)W/dalvikvm(19617):505:0x4051c438 cls=Ltw/ksana/javacpp/javacpp$MYView;(252字节)也许我应该如何处理返回的句柄
查看
一些方法?谢谢。但是,如果没有PRINTF(),程序也会崩溃。另外一种可能性是:是否必须清理/关闭/返回从findViewById返回的值?DeleteLocalRef工作得非常出色!有趣的是,你提到JS,因为我实际上是在混合JS和Java。在v8中,我用于声明
v8::HandleScope handle\u范围
处理VM GC,但Java有其他方法,如您所述。非常感谢。
#define PRINTF(format, ...) \
  ((void)__android_log_print(ANDROID_LOG_INFO, "yaui", format, __VA_ARGS__))