JNI內存管理之Local Reference 和 Global Reference知識點
最近開發(fā)過程中遇到了JNI的Reference相關問題,了解到Local Reference和Global Reference的相關知識點,整理如下:
背景:項目需求,在Native C/C++層調用上層Android Camera Java接口,把所有的操作包括Camera都沉到Native層去實現(xiàn)。但在JNI調試過程中遇到了android JNI ERROR (app bug): accessed stale local reference的報錯。
現(xiàn)象:在Native層創(chuàng)建Java的Camera對象,其對象的指針保存到本地,函數(shù)返回到Java層,之后再進入Native層,想通過Native層的Camera對象指針調用相應的方法,但是發(fā)現(xiàn)每次都是重新調用Java對象方法后報錯。
分析:在Native層創(chuàng)建的Java對象,對象創(chuàng)建后會有一個局部引用指向該對象,當從Native環(huán)境返回到Java環(huán)境,該局部引用失效,此對象就沒有引用計數(shù),Java的內存回收機制會自動回收該對象,第二次再進入Native層訪問其之前保存的地址時就會報錯。
解決:使用全局引用始終持有該對象的引用使其不被自動回收,請看下面的知識點。
Local Reference
局部引用,看如下精簡代碼:
"0"); NewStringUTF(
在JNI中,每次調用NewObject方法創(chuàng)建一個新的對象都會返回一個對該對象的局部引用(Local Reference),該局部引用只在線程當前的Native環(huán)境中有效,返回到Java環(huán)境后該引用與對象之間的聯(lián)系就會被斷掉,引用失效,所以我們不能在Native方法中把局部引用緩存用于下一次調用時使用。
局部引用可以無限創(chuàng)建嗎?
如圖:
這里引入局部引用表的概念,每當線程從Java環(huán)境進入到Native環(huán)境后,JVM就會創(chuàng)建該線程Native環(huán)境的局部引用表,用來保存本次Native環(huán)境所創(chuàng)建的所有局部引用,每當Native中引用或者新創(chuàng)建一個Java對象,JVM就會局部引用表創(chuàng)建一個局部引用,局部引用表是有大小限制的,最大是512,如果超過限制會報OOM內存泄漏。
Q:那如何才能更好的避免由于局部引用過多造成Native環(huán)境中的OOM呢?
A:控制局部引用的生命周期,如果需要創(chuàng)建過多的局部引用,可以在Java對象的操作結束后,手動調用DeleteLocalRef函數(shù)刪除局部引用,該局部引用就會在局部引用表中被移除,避免觸發(fā)局部引用表的大小限制。
注意:局部引用不是我們平時所理解的代碼中的局部變量,局部變量在當前生命周期(例如函數(shù)退出)結束后就會失效,而局部引用在函數(shù)退出后可能不會失效,它的生命周期是和整個Native上下文環(huán)境相關聯(lián),只有從Native環(huán)境返回到Java環(huán)境后局部引用才會失效。
Global Reference
全局引用,終于到了最上面討論的問題了,因為局部引用在Native環(huán)境返回到Java環(huán)境后就會失效,導致下次進入Native環(huán)境后再次使用相對應的Java對象就會出錯,所以可以使用全局引用來解決這個問題,全局引用可以始終與Java對象保持聯(lián)系,使得此對象不會被JVM回收掉,見如下代碼:
JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {
jclass tmp = env->FindClass("com/example/company/MyClass");
jclass class = env->NewGlobalRef(tmp);
return JNI_VERSION_1_6;
}
這里需要注意,在不需要使用Java對象后盡量手動調用DeleteGlobalRef()函數(shù)來使得引用失效,避免對象始終存在,產生潛在的內存泄漏。
Weak Global Reference
虛全局引用與全局引用的區(qū)別在于該類型的引用可能隨時被JVM回收掉,這里涉及到幾個函數(shù):
NewWeakGlobalRef();
DeleteWeakGlobalRef();
isSameObject();
在使用虛引用前需要通過isSameObject將其和NULL比較,如果返回true表示已經被JVM回收掉就不能使用了,這里有可能前一行代碼判斷還是可用,后一行代碼時就被JVM回收,解決辦法是通過NewLocalRef()獲取虛全局引用,避免當時被JVM回收。
參考資料
https://www.cnblogs.com/zhongshujunqia/p/4638077.html?utm_source=tuicool&utm_medium=referral
https://www.cnblogs.com/younghome/p/4609044.html
https://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
https://www.ibm.com/developerworks/cn/java/j-lo-jnileak/index.html
https://juejin.im/post/5c19bfa0f265da6133568545
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!