- Java 與JNI 內(nèi)存管理是怎樣的
想要弄清楚Java與JNI的內(nèi)存管理的關系引颈,首先要弄清楚JVM的內(nèi)存模型
其中本地方法棧就是運行時調(diào)用native 方法的數(shù)據(jù)保存區(qū)沦童。
本地方法棧的大小可以設置成固定的或者是動態(tài)擴展。
- Java中的內(nèi)存泄露
JAVA 編程中的內(nèi)存泄漏撑教,從泄漏的內(nèi)存位置角度可以分為兩種:JVM 中 Java Heap 的內(nèi)存泄漏朝墩;JVM 內(nèi)存中 native memory 的內(nèi)存泄漏。
-
Java Heap 的內(nèi)存泄漏
- Java 對象存儲在 JVM 進程空間中的 Java Heap 中伟姐,Java Heap 可以在 JVM 運行過程中動態(tài)變化收苏。如果 Java 對象越來越多亿卤,占據(jù) Java Heap 的空間也越來越大,JVM 會在運行時擴充 Java Heap 的容量鹿霸。如果 Java Heap 容量擴充到上限排吴,并且在 GC 后仍然沒有足夠空間分配新的 Java 對象,便會拋出 out of memory 異常懦鼠,導致 JVM 進程崩潰钻哩。
- Java Heap 中 out of memory 異常的出現(xiàn)有兩種原因:①程序過于龐大,致使過多 Java 對象的同時存在肛冶;②程序編寫的錯誤導致 Java Heap 內(nèi)存泄漏憋槐。多種原因可能導致 Java Heap 內(nèi)存泄漏。JNI 編程錯誤也可能導致 Java Heap 的內(nèi)存泄漏淑趾。
-
JVM 中 native memory 的內(nèi)存泄漏
JVM 進程空間中阳仔,Java Heap 以外的內(nèi)存空間稱為 JVM 的 native memory。進程的很多資源都是存儲在 JVM 的 native memory 中扣泊,例如載入的代碼映像近范,線程的堆棧,線程的管理控制塊延蟹,JVM 的靜態(tài)數(shù)據(jù)评矩、全局數(shù)據(jù)等等。也包括 JNI 程序中 native code 分配到的資源阱飘。JNI編程中的內(nèi)存泄露
Native Code 本身的內(nèi)存泄漏
Global Reference 引入的內(nèi)存泄漏
JNI 編程中潛在的內(nèi)存泄漏斥杜,特別是LocalReference的使用
-
哪些情況下需要做內(nèi)存管理?
- native code本身需要做內(nèi)存管理
如使用c/c++語言編寫沥匈,需要遵守語言本身的內(nèi)存管理策略蔗喂,如指針變量的創(chuàng)建于釋放不當都可能產(chǎn)生內(nèi)存泄露,進一步導致內(nèi)存JVM的崩潰高帖。
- Global Reference的管理
而 Global Reference 對 Java 對象的引用一直有效缰儿,因此它們引用的 Java 對象會一直存在 Java Heap 中。在使用 Global Reference 時散址,需要仔細維護對 Global Reference 的使用乖阵。如果一定要使用 Global Reference,務必確保在不用的時候刪除预麸。就像在 C 語言中瞪浸,調(diào)用 malloc() 動態(tài)分配一塊內(nèi)存之后,調(diào)用 free() 釋放一樣吏祸。否則对蒲,Global Reference 引用的 Java 對象將永遠停留在 Java Heap 中,造成 Java Heap 的內(nèi)存泄漏。
-
Local Reference的管理
Local Reference管理模型圖
localReference_Java_map.jpg
從這個映射關系表可以看出齐蔽,實際上,每當線程從 Java 環(huán)境切換到 native code 上下文時(J2N)床估,JVM 會分配一塊內(nèi)存含滴,創(chuàng)建一個 Local Reference 表,這個表用來存放本次 native method 執(zhí)行中創(chuàng)建的所有的 Local Reference丐巫。每當在 native code 中引用到一個 Java 對象時谈况,JVM 就會在這個表中創(chuàng)建一個 Local Reference:
運行 native method 的線程的堆棧記錄著 Local Reference 表的內(nèi)存位置(指針 p)
Local Reference 表中存放 JNI Local Reference,實現(xiàn) Local Reference 到 Java 對象的映射递胧。
native method 代碼間接訪問 Java 對象(java obj1碑韵,java obj2)。通過指針 p 定位相應的 Local Reference 的位置缎脾,然后通過相應的 Local Reference 映射到 Java 對象祝闻。
當 native method 引用一個 Java 對象時,會在 Local Reference 表中創(chuàng)建一個新 Local Reference遗菠。在 Local Reference 結構中寫入內(nèi)容联喘,實現(xiàn) Local Reference 到 Java 對象的映射。
native method 調(diào)用 DeleteLocalRef() 釋放某個 JNI Local Reference 時辙纬,首先通過指針 p 定位相應的 Local Reference 在 Local Ref 表中的位置豁遭,然后從 Local Ref 表中刪除該 Local Reference,也就取消了對相應 Java 對象的引用(Ref count 減 1)贺拣。
當越來越多的 Local Reference 被創(chuàng)建蓖谢,這些 Local Reference 會在 Local Ref 表中占據(jù)越來越多內(nèi)存。當 Local Reference 太多以至于 Local Ref 表的空間被用光譬涡,JVM 會拋出異常闪幽,從而導致 JVM 的崩潰。
Local Reference 不是native code的局部變量涡匀,區(qū)別體現(xiàn)在:
局部變量存儲在線程堆棧中沟使,而 Local Reference 存儲在 Local Ref 表中。
局部變量在函數(shù)退棧后被刪除渊跋,而 Local Reference 在調(diào)用 DeleteLocalRef() 后才會從 Local Ref 表中刪除腊嗡,并且失效,或者在整個 Native Method 執(zhí)行結束后被刪除拾酝。
可以在代碼中直接訪問局部變量燕少,而 Local Reference 的內(nèi)容無法在代碼中直接訪問,必須通過 JNI function 間接訪問蒿囤。JNI function 實現(xiàn)了對 Local Reference 的間接訪問客们,JNI function 的內(nèi)部實現(xiàn)依賴于具體 JVM。
具體關于JNI內(nèi)存泄露的實例分析可以參考IBM開發(fā)者社區(qū)的一篇文章:在 JNI 編程中避免內(nèi)存泄漏