天天看點

Android NDK開發系列教程5:局部引用,全局引用,弱全局引用

從Java虛拟機建立的對象當傳入到native層時會産生一個引用,在進行垃圾回收時如果有native的引用,改對象同樣也不會被回收。在native引用中分局部引用和全局引用。

局部引用又稱本地引用,大多數見到的引用都是局部引用,例如通過NewLocalRef和各種JNI接口建立(FindClass、NewObject、GetObjectClass和NewCharArray等),局部引用隻會在本次native調用中有效,當本次調用結束後該引用即被自動釋放。局部引用會阻止GC進行回收。同時也可以調用DeleteLocalRef函數來手動釋放(比如在循環裡面用到了局部引用而退出循環沒有使用該局部引用,那麼就需要在循環中釋放該局部引用)。通常使用NewObject建立的執行個體傳回的也是局部引用。千萬不要把局部引用儲存為c++的全局變量或者把它定義為靜态變量,局部引用的有效期是一次Java本地調用。

JNI提供了一系列函數來管理局部引用的生命周期。這些函數包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

PushLocalFrame為目前函數中局部引用建立了一個引用堆棧,在每周遊一次調用(*env)->GetObjectArrayElement(env, arr, i);傳回一個局部引用時,JVM會自動将該引用壓入目前局部引用棧中。而PopLocalFrame負責将棧中所有引用釋放。這樣一來,Push/PopLocalFrame函數對提供了對局部引用生命周期更友善的管理,不用再去一個個Delete了。

全局引用可以在目前線程使用,也可以在其他線程使用,可以儲存在本地的static靜态變量或全局變量中,全局引用需要調用NewGlobalRel函數建立,釋放時采用ReleaseGlobalRef函數釋放。有效作用域在建立後,一直到調用ReleaseGlobalRef釋放時。

在Java1.2中,新增了弱全局引用,與全局變量一樣其建立、删除均需要程式設計寫出,也可以在本地多個代碼中使用,也可以跨程序使用。不一樣的是,它的存在不影響垃圾回收機制對該引用所指向對象執行個體的回收。其建立采用NewWeakGlobalRef,釋放采用ReleaseWeakGlobalRel。

以上涉及的函數主要有以下幾個:

上述三中引用會影響記憶體的回收,在C/C++中沒有向Java一樣的垃圾回收機制,自己申請的記憶體要記得自己去釋放了,否則會導緻記憶體洩漏。雖然現在C/C++裡面也有智能指針,但相對而言這個智能指針用起來不如Java。是以在C的世界裡要遵循誰申請,誰釋放的基本原則。

上面介紹了基本知識,下面給出相應的例子來進行說明下。

在局部引用中要注意以下幾方面:

1. 循環體内建立的局部引用,要在循環體内就直接釋放了。

2. 編寫的工具函數,裡面建立的局部引用,要在該工具函數裡面釋放了。

3. 局部引用引用了一個大的Java對象,這時候一定一定要早點釋放了。

4. 局部引用不要緩存在native層

弱全局引用和全局引用基本差不多,最大的差別就是弱全局引用不影響GC的回收。在使用弱全局引用的時候一定要注意,使用前要檢查下是不是被GC回收了。

jni提供了相應的函數

如果兩個引用指向同一個執行個體則傳回JNI_TRUE,否則傳回JNI_FALSE。