【问题标题】:Android JNI - Call AttachCurrentThread without DetachCurrentThreadAndroid JNI - 在没有 DetachCurrentThread 的情况下调用 AttachCurrentThread
【发布时间】:2014-12-19 11:42:38
【问题描述】:

我一直在阅读有关 JNI 的内容,但似乎无法弄清楚如果线程启动 -> 调用 AttachCurrentThread() -> 进行一些 JNI 调用 -> 线程退出会发生什么。

理想情况下,我们应该在线程退出之前调用 DetachCurrentThread(),但是,如果应用程序不这样做,会有什么影响?它会导致内存泄漏或任何其他问题吗?

【问题讨论】:

  • 请注意,如果您附加了线程,则只能调用DetachCurrentThread()。如果当前线程是 JVM 拥有的线程,则分离当前线程的行为是未定义的。

标签: android android-ndk java-native-interface


【解决方案1】:

不调用DetachCurrentThread()肯定会导致内存泄漏;其他后果是特定于 JVM 的,可能与 Android 应用程序无关,其中 JVM 在进程退出时关闭。有很多 C++ 包装器可以帮助管理线程 Attach/Detach,例如:http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier

更新:1000 感谢 fadden 大开眼界link;在 Dalvik 上,一个没有调用 DetachCurrentThread() 就退出的线程会导致整个 VM 和进程崩溃。

这是来自官方模拟器的 logcat,我的代码基于 NDK 的HelloJni 示例:

10-26 04:16:25.853: D/dalvikvm(1554): Trying to load lib /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0
10-26 04:16:25.893: D/dalvikvm(1554): Added shared lib /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0
10-26 04:16:25.893: D/dalvikvm(1554): No JNI_OnLoad found in /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0, skipping init
10-26 04:16:26.463: D/gralloc_goldfish(1554): Emulator without GPU emulation detected.
10-26 04:16:31.033: D/threadFunction(1554): Attaching
10-26 04:16:31.173: D/threadFunction(1554): Not Detaching
10-26 04:16:31.183: D/dalvikvm(1554): threadid=11: thread exiting, not yet detached (count=0)
10-26 04:16:31.193: D/dalvikvm(1554): threadid=11: thread exiting, not yet detached (count=1)
10-26 04:16:31.193: E/dalvikvm(1554): threadid=11: native thread exited without detaching
10-26 04:16:31.193: E/dalvikvm(1554): VM aborting
10-26 04:16:31.213: A/libc(1554): Fatal signal 6 (SIGABRT) at 0x00000612 (code=-6), thread 1567 (xample.hellojni)

这里是添加到hello-jni.c的相关功能:

static JavaVM* jvm = 0;
static jobject activity = 0; // GlobalRef

void* threadFunction(void* irrelevant)
{
    JNIEnv* env;
    usleep(5000000);

    __android_log_print(ANDROID_LOG_DEBUG, "threadFunction", "Attaching");

    (*jvm)->AttachCurrentThread(jvm, &env, NULL);

    jclass clazz = (*env)->GetObjectClass(env, activity);
    jmethodID methodID = (*env)->GetMethodID(env, clazz, "finish", "()V" );
    (*env)->CallVoidMethod(env, activity, methodID);

    __android_log_print(ANDROID_LOG_DEBUG, "threadFunction", "Not Detaching");
//    (*jvm)->DetachCurrentThread(jvm);
}

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    (*env)->GetJavaVM(env, &jvm);
    activity = (*env)->NewGlobalRef(env, thiz);

    pthread_t hThread;
    pthread_create(&hThread, NULL, &threadFunction, NULL);
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

可以在 WebRTC git repo 中找到该策略的一个很好的实现。

【讨论】:

  • 我无法访问链接。
  • 另外,如果我创建大量线程并在没有分离的情况下调用 attachcurrentthread,则可能存在内存泄漏。但是,如果有几个长时间运行的线程并且它们在没有调用 detachcurrentthread 的情况下退出,会发生什么情况。会有什么影响?退出后不会自动分离吗?
  • 如果线程附加然后停止但没有分离,Dalivk VM 将中止,因此资源泄漏不会被忽视。看一下 android.googlesource.com/platform/dalvik/+/kitkat-release/vm/… 中的 threadExitCheck(),第 1035 行。
  • @fadden: 1000 谢谢,真的!我刚试过这个,是的,当我删除一个无辜的DetachCurrentThread() 电话时,应用程序崩溃了。请查看更新。
  • 一个注意事项:通过阅读 JNI 源代码,很明显附加/分离不是递归的。所以你应该先打电话给GetEnv(),如果它失败了只打电话给AttachCurrentThread()/DetachCurrentThread()。否则,您将处于这样一种情况,即在已经存在有效 JNIEnv 但您将其分离的范围内调用了一个函数,现在随后的任何函数都被搞砸了。 (如果我遗漏了什么,请有人纠正我。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 2012-07-21
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多