android 內存回收


  昨天朋友問我,如果一個java局部對象在調用jni的時候,如果java層沒有引用它,這個對象會不會因為被jni層引用不被GC,導致內存泄漏。我大概想了一下,說不會。當時想的很簡單,c里面沒有像java一樣的類似的內存回收機制,java層進入jni時值傳遞,不會導致引用產生。實事上比想象的復雜的多,而且並不是這樣的。  

  按照這種想法,如果java層的對象被jni引用時不會計數,對象被GC時,jni層會產生野指針。事實並不是這樣。

  首先GC是發生在進程范圍內的堆空間,如果堆里的對象沒有被引用,是要被GC回收的。jni層也會在堆上創建對象,照樣能被GC。

  java的內存回收可以用下圖表示。

  

如果heap里面的對象沒有被Stack引用到,即紅色的區域,是要被回收的。總的來說,只要對象沒有被GC root直接或間接引用到就會被GC。

jni調用產生時,jvm的內存分布是這個樣子的。

它有兩個棧,一個是java棧,另外一個是native 棧。當一個java調用jni時,棧幀布局其實是下圖這個樣子。

這個是棧幀示意圖,上圖所示,該線程首先調用了兩個Java方法,而第二個Java方法又調用了一個本地方法,這樣導致虛擬機使用了一個本地方法棧。圖中的本地方法棧顯示為 一個連續的內存空間。假設這是一個C語言棧,期間有兩個C函數,他們都以包圍在虛線中的灰色塊表示。第一個C函數被第二個Java方法當做本地方法調用, 而這個C函數又調用了第二個C函數。之后第二個C函數被第二個Java方法當做本地方法調用,而這個C函數又調用了第二個C函數。之后第二個C函數又通過 本地方法接口回調了一個Java方法(第三個Java方法)。最終這個Java方法又調用了一個Java方法(他成為圖中的當前方法) 。

 

剛才說到只要對象沒有被GC root直接或間接引用到就會被GC。GC root到底是什么呢?

JVM對那些沒有根引用的對象進行來及回收,也就是無法從根對象中追述的對象。
JVM垃圾回收的根對象的范圍有以下幾種:
1、棧中引用的對象,引用是在棧幀中的本地變量表中的,真正的對象在堆中
2、方法區perm中的類靜態屬性引用的對象,以及常量引用的對象
3、本地方法棧中JNI(Native方法)的引用的對象

所以jni層,C函數調用C函數傳遞jobject對象時,對象的引用會被作為參數壓入到本地方法棧中,會從本地方法棧(GC root的一種)產生引用,這么就不會被gc了。同樣java層和jni層互相調用時,總會被GC root引用到,也不會被GC 回收了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM