Jni频繁资源未释放引起的崩溃问题

Failed adding to JNI pinned array ref table (1024 entries)

问题重现:

在开发蓝牙模块升级的时候, 由于要传送的升级文件较大,所以需要频繁的通过JNI调用C语言来组装报文,结果在低版本手机测试时遇到Failed adding to JNI pinned array ref table (1024 entries).

错误代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
JNIEXPORT jbyteArray JNICALL xxx_BleUtils_sendUpdatePkt(
JNIEnv *env, jclass jobj, jbyteArray pkt, jint pkt_sn, jint pktLen, jint token
) {
unsigned long outbuf[APPAPI_MAXSENDLEN / 4];
unsigned char *pBuffer = (*env)->GetByteArrayElements(env, pkt, NULL); //<==引起错误的地方
int ret = ynLockSendPkt((uint16_t) token, (uint16_t) pkt_sn, pktLen,
pBuffer,
(char *) outbuf,
APPAPI_MAXSENDLEN);
jbyteArray array = (*env)->NewByteArray(env, ret);
(*env)->SetByteArrayRegion(env, array, 0, ret, outbuf);
return array;
}

原因:

运行 native method 的线程的堆栈记录着 Local Reference 表的内存位置(指针 p),Local Reference表中存放JNI Local Reference实现 Local Reference 到 Java 对象的映射,函数中调用的GetByteArrayElements()会使Local Reference表中的存放的指针加一,频繁的调用而得不到释放的话表中的值就会一直增加,直至内存被占满引起崩溃。

解决方法:

在函数中return前释放资源:

1
(*env)->ReleaseByteArrayElements(env,pkt, pBuffer, 0); //pkt为java层传递过来的数组,pBuffer为指针

由于是在JNI层发生的错误,而且项目只是集成了bugly在Java层面的错误收集,所以一直找不到崩溃原因,最后不得不找了同型号的手机来测试几遍才发现问题,看来BUG收集也要尽善尽美才行啊,不能偷懒。

只在低配手机出现没有在高配手机出现的原因应该是高配手机分配的内存更大。但是这里的内存只要不被释放,当以后升级包更大的时候总有一天高配手机也会出现问题的。