最近開發過程當中遇到了JNI的Reference相關問題,瞭解到Local Reference和Global Reference的相關知識點,整理以下:html
背景:項目需求,在Native C/C++層調用上層Android Camera Java接口,把全部的操做包括Camera都沉到Native層去實現。但在JNI調試過程當中遇到了android JNI ERROR (app bug): accessed stale local reference的報錯。java
現象:在Native層建立Java的Camera對象,其對象的指針保存到本地,函數返回到Java層,以後再進入Native層,想經過Native層的Camera對象指針調用相應的方法,可是發現每次都是從新調用Java對象方法後報錯。android
分析:在Native層建立的Java對象,對象建立後會有一個局部引用指向該對象,當從Native環境返回到Java環境,該局部引用失效,此對象就沒有引用計數,Java的內存回收機制會自動回收該對象,第二次再進入Native層訪問其以前保存的地址時就會報錯。c++
解決:使用全局引用始終持有該對象的引用使其不被自動回收,請看下面的知識點。緩存
局部引用,看以下精簡代碼:app
env->NewStringUTF("0");
在JNI中,每次調用NewObject方法建立一個新的對象都會返回一個對該對象的局部引用(Local Reference),該局部引用只在線程當前的Native環境中有效,返回到Java環境後該引用與對象之間的聯繫就會被斷掉,引用失效,因此咱們不能在Native方法中把局部引用緩存用於下一次調用時使用。函數
局部引用能夠無限建立嗎post
如圖:ui
這裏引入局部引用表的概念,每當線程從Java環境進入到Native環境後,JVM就會建立該線程Native環境的局部引用表,用來保存本次Native環境所建立的全部局部引用,每當Native中引用或者新建立一個Java對象,JVM就會局部引用表建立一個局部引用,局部引用表是有大小限制的,最大是512,若是超過限制會報OOM內存泄漏。spa
Q:那如何才能更好的避免因爲局部引用過多形成Native環境中的OOM呢?
A:控制局部引用的生命週期,若是須要建立過多的局部引用,能夠在Java對象的操做結束後,手動調用DeleteLocalRef函數刪除局部引用,該局部引用就會在局部引用表中被移除,避免觸發局部引用表的大小限制。
注意:局部引用不是咱們平時所理解的代碼中的局部變量,局部變量在當前生命週期(例如函數退出)結束後就會失效,而局部引用在函數退出後可能不會失效,它的生命週期是和整個Native上下文環境相關聯,只有從Native環境返回到Java環境後局部引用纔會失效。
全局引用,終於到了最上面討論的問題了,由於局部引用在Native環境返回到Java環境後就會失效,致使下次進入Native環境後再次使用相對應的Java對象就會出錯,因此可使用全局引用來解決這個問題,全局引用能夠始終與Java對象保持聯繫,使得此對象不會被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()函數來使得引用失效,避免對象始終存在,產生潛在的內存泄漏。
虛全局引用與全局引用的區別在於該類型的引用可能隨時被JVM回收掉,這裏涉及到幾個函數:
NewWeakGlobalRef(); DeleteWeakGlobalRef(); isSameObject();
在使用虛引用前須要經過isSameObject將其和NULL比較,若是返回TRUE返回true表示已經被JVM回收掉就不能使用了,這裏有可能前一行代碼判斷仍是可用,後一行代碼時就被JVM回收,解決辦法時經過NewLocalRef()獲取虛全局引用,避免當時被JVM回收。
https://www.cnblogs.com/zhong...
https://www.cnblogs.com/young...
https://stackoverflow.com/que...
https://www.ibm.com/developer...
https://juejin.im/post/5c19bf...
更多文章,請關注個人V X 公 主 號:程序喵大人,歡迎交流。