Android NDK中多线程JNI的local reference释放问题

首先声明这个问题是在Android系统中进行JNI调用时发现的,不确定是标准Java行为还是Android的特例,非Android系统仅供参考!

之前在做NDK开发时,自然少不了JNI的Java->C/C++以及C/C++->Java的互相调用过程,如一些常见的GetStringUTFChars,GetObjectClass等等,对VM的Java对象引用计数也略有一些概念,像GetStringUTFChars需要ReleaseStringUTFChars,DeleteLocalRef释放局部引用计数等,不过大部分情况下,没有DeleteLocalRef也没出什么太大的问题,这次在试验NDK的C+11支持时用到了std::thread和lambda表达式创建了native线程,如下:

workerThread = std::thread([this]()
{
    g_pVM->AttachCurrentThread(&mJNIEnv, NULL);
    ...
    g_pVM->DetachCurrentThread();
}
);

并在线程中进行了Java方法的调用,传递了一些字符串,结果没跑几个循环进程就crash掉了,看了下logcat提示的错误是:

JNI ERROR (app bug): local reference table overflow (max=512)

并打印出了最后200个local reference table中的东西,看了下有传的String有get到的Java class,于是想到了释放引用的问题,不过JNI调用的代码都是copy过来以前写的,也没出现过类似的问题,很是奇怪。

看着table里的东西把该release的都release了,用完了的object都DeleteLocalRef了,再运行了几次果然不再crash了。看来对JNI的理解需要加强一下了,开始寻找资料,最后在谷沟Android开发官网找到的一篇JNI Tips(链接见下方)中找到了相关信息:

One unusual case deserves separate mention. If you attach a native thread with AttachCurrentThread, the code you are running will never automatically free local references until the thread detaches. Any local references you create will have to be deleted manually. In general, any native code that creates local references in a loop probably needs to do some manual deletion.

原来native代码中创建线程并attach出的JNIEnv不会自动释放局部引用,直到detach,所以如果这之间的ref没太多的,以至于超过max=512的话,撑到detach也就释放相安无事了,但要是像我这样有个循环不停的进行JNI调用的话肯定没一会就超额了,所以必要的手动释放ref还是要加上的。
参考:

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。