Jni 記憶體洩露(Failed adding to JNI pinned array ref table (1024 entries))
問題重現
Failed adding to JNI pinned array ref table (1024 entries)
在開發藍芽模組升級的時候, 由於要傳送的升級檔案較大,而 BLE 模組一次傳輸資料大小有限制,因此需要拆包,並需要頻繁的通過JNI呼叫so庫來組裝報文,結果在低版本手機測試時遇到Failed adding to JNI pinned array ref table (1024 entries).
錯誤程式碼
下面是出現錯誤的函式:
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;
}
解決辦法
查詢官方文件也可以知道需要通過 ReleaseByteArrayElements
來及時的釋放資源:
(*env)->ReleaseByteArrayElements(env,pkt, pBuffer, 0); //pkt為java層傳遞過來的陣列,pBuffer為指標
原因
其實 GetByteArrayElements 就類似於一個 new 的操作,而在 new 以後沒有進行釋放,而 C 並不會自動的去回收這些內容,所以頻繁呼叫的情況下,引用次數不斷增加,最終導致溢位。
後來查閱資料發現,上面的說法並不準確,首先 GetByteArrayElements 會創造出一個區域性引用,JNI 會將建立的區域性引用都儲存在一個區域性引用表中,如果這個表超過了最大容量限制,就會造成區域性引用表溢位(比如本例中的 1024,在配置更低的手機中這個值可能更小),使程式崩潰。但是關於區域性引用的釋放問題,除了我們可以手動進行釋放以外,函式被呼叫完成後,JVM 會自動釋放函式中建立的所有區域性引用。
那麼在上面的錯誤程式碼中,很顯然方法執行結束後會自動釋放 GetByteArrayElements 所建立的區域性引用,而實驗證明確實是這樣:
java 測試程式碼:
byte[] b = {0x00};
public void test() {
for (int i = 0; i < 20000; i++) {
System.out.println("aaaaaa::" + i);
EAJniUtils.test2(b);
}
}
JNI 程式碼:
JNIEXPORT jbyteArray JNICALL xxx_utils_EAJniUtils_test2
(JNIEnv *env, jclass jobj, jbyteArray pkt){
jbyte *pBuffer = (*env)->GetByteArrayElements(env, pkt, 0);
int bufLen = (*env)->GetArrayLength(env,pkt);
jbyteArray array = (*env)->NewByteArray(env, bufLen);
(*env)->SetByteArrayRegion(env, array, 0, bufLen, pBuffer);
return array;
}
可以看到上面 test2 函式沒有釋放區域性變數,而在 java 層對他進行了 20000 此迴圈呼叫,程式沒有崩潰。(分別在魅族MX5(andorid 5.0)和一加3T(android 8.0)上進行的測試)
因此正確的原因應該是在原始程式碼的ynLockSendPkt
函式中某個地方持有了 pBuffer 指標,導致它一直不被釋放,所以我們後面手動的對它進行了釋放。然而事實上問題到這裡並沒有完全被解決,因為是引入的第三方so所以也沒辦法看到具體原因,但是至少知道了問題出在什麼地方。
以上是針對這個問題的個人看法,如果錯誤之處還請指正。
參考資料
相關推薦
Jni 記憶體洩露(Failed adding to JNI pinned array ref table (1024 entries))
問題重現 錯誤程式碼 解決辦法 原因 問題重現 Failed adding to JNI pinned array ref table (1024 entries) 在開發藍芽模組升級的時候, 由於要傳送
jni未釋放資源問題。Failed adding to JNI local ref table (has 512 entries)
基於 Android NDK 的學習之旅-----資源釋放 做上一個專案的時候因為與C引擎互動頻繁,有時候會突然莫名其妙的的整個應用程式直接掛掉。因為我是學Java 開始的,所以對主動釋放記憶體沒多大概念(GC直接幫忙回收),後查詢原因才知道是因為JNI
JNI記憶體洩露處理方法彙總
在c++中new的物件,如果不返回java,必須用release掉,否則記憶體洩露。包括NewStringUTF,NewObject。如果返回java不必release,java會自己回收。jstring jstr = env->NewStringUTF((*p).s
jni 記憶體洩露 local reference table overflow (max=512)
01-02 00:02:35.064: E/dalvikvm(4223): JNI ERROR (app bug): local reference table overflow (max=512) 向JNI傳遞大量的資料,或new出大量物件時,如果不及時釋放,則會造成上述
解決failed to unregister JDBC driver導致可能記憶體洩露的問題
將mysql的驅動包mysql-connector-java-5.1.39從webContent->web-inf->lib移到tomcat的lib目錄下,既可以簡化新建web專案時的導包麻煩,也能解決專案關閉時記憶體洩露的問題 The web application [] registered
JNI記憶體釋放以及洩露處理方法彙總
在c++中new的物件,如果不返回java,必須用release掉,否則記憶體洩露。包括NewStringUTF,NewObject 。如果返回java不必release,java會自己回
JNI手動釋放記憶體(避免記憶體洩露)
1. 哪些需要手動釋放? 不要手動釋放(基本型別): jint , jlong , jchar 需要手動釋放(引用型別,陣列家族): jstring,jobject ,jobjectArray,
JNI記憶體管理及優化
JVM記憶體和Native記憶體 上面這張圖大家都應該很熟了,下面只講下和JNI有關的部分 程式計數器 記錄正在執行的虛擬機器位元組碼指令的地址(如果正在執行的是本地方法則為空)。 本地方法棧 本地方法棧與 Java 虛擬機器棧類似,它們之間的區別只不過是本地方法棧為本地方法服務。
JNI記憶體
一般情況: JNI 基本資料型別是不需要釋放的 , 如 jint , jlong , jchar 等等 。 我們需要釋放是引用資料型別,當然也包括陣列家族。如:jstring ,jobject ,jobjectArray,jintArray 等等。 其他情況
7.JNI 記憶體洩漏 處理 方法總結
在c++中new的物件,如果不返回java,必須用release掉,否則記憶體洩露。包括NewStringUTF,NewObject。如果返回java不必release,java會自己回收。 jstring jstr = env->NewStringUTF((*p).
JNI記憶體管理
D/testabc: i=501 D/testabc: i=502 D/testabc: i=503 D/testabc: i=504 A/art: JNI ERROR (app bug): local reference table overflow (max=512) A/art: local ref
【已解決】執行Eclipse出錯:Failed toload the JNI shared library
【已解決】執行Eclipse出錯:Failed toload the JNI shared library 【問題】 執行Android的ADT,即Eclipse出錯: Failed to load the JNIshared library C:\Program File
Execution default of goal org.springframework.boot:spring-boot-maven-plugin:1.5.6.RELEASE:repackage failed: Unable to find main class
spl final package exce main project clas ini exit 異常 [INFO] --- spring-boot-maven-plugin:1.5.6.RELEASE:repackage (default) @ spring-boot
Handshake failed due to invalid Upgrade header: null 解決方案
led ade cal lis ex18 解決 代碼 number border 解決方案,在 Nginx ,location 中添加以下紅色代碼: proxy_set_header Upgrade $http_upgrade; proxy_set_header C
Context []startup failed due to previous errors 問題解決
信息 log4j bug sse err led jar logger debug log4j.rootLogger=info,Console,R log4j.appender.Console=org.apache.log4j.ConsoleAppender
curl: (7) Failed connect to 172.16.100.199:9200; 沒有到主機的路由
ble stat ber ret iptable stop int iter OS 沒有到主機的路由這種問題很常見,多數是由機器的防火墻沒有關閉。 Ubuntu 查看防火墻狀態 ufw status 關閉防火墻 ufw disable centos6 查看防火墻狀
jQuery清空標籤內容--防止記憶體洩露
寫jQuery程式碼是,經常會做清空一個標籤內容的操作。那麼你是怎麼做的呢?比如 <div id="box"> <p>星期一</p> <p>星期二</p> <p>星期三</p> &l
9、【C++】記憶體洩露
記憶體洩露 1、記憶體洩露的定義 一般我們常說的記憶體洩漏是指堆記憶體的洩漏。堆記憶體是指程式從堆中分配的,大小任意的(記憶體塊的大小可以在程式執行期決定),使用完後必須顯示釋放的記憶體。 應用程式一般使用malloc,realloc,new等函式從堆中分配到一塊
Android記憶體洩露分析
一,記憶體洩露 記憶體洩露:一個不在被使用的物件被另一個存活著的物件引用,在這種情況下垃圾回收器會跳過他,因為這種引用關係足以讓該物件駐留在記憶體中,記憶體洩露是在組織垃圾回收器為未來的記憶體分配提供空間,這些洩露的物件一直佔據著記憶體,導致我們的堆記憶體空間變得更小。也加劇了垃圾回
使用 GC、Objgraph 幹掉 Python 記憶體洩露與迴圈引用
Python使用引用計數和垃圾回收來做記憶體管理,前面也寫過一遍文章《Python記憶體優化》,介紹了在python中,如何profile記憶體使用情況,並做出相應的優化。本文介紹兩個更致命的問題:記憶體洩露與迴圈引用。記憶體洩露是讓所有程式設計師都聞風喪膽的問題,輕則導致程式執行速度減慢,重則導致