JNI開發之JNI內存泄露

  在上篇文章中介紹了JNI常見錯誤,這篇文章將描述JNI開發中內存泄露問題。在Java編程中,內存泄漏可以根據泄漏的內存位置劃分爲兩種:一種是JVM中的Java Heap的內存泄漏。另外一種是JVM中的Native memory內存泄漏。

一、Java Heap內存泄漏

  Java對象存儲在JVM進程空間中的Java Heap中,Java Heap可以在JVM運行過程中動態變化。如果Java對象越來越多,佔據Java Heap的空間也越來越大,JVM會在運行時動態擴展Java Heap的容量。如果Java Heap容量擴充到了上限,並且GC後仍然沒有足夠的空間分配新的Java對象,便會拋出OutOfMemory錯誤,導致程序崩潰。

  
二、Native memory內存泄漏

  從操作系統角度來看,JVM在運行時和其他進程沒有本質的區別。在系統級別上,它們具有同樣的調度機制,同樣的內存分配方式,同樣的內存格局。多種原因可能導致JVM的native memory內存泄漏。例如:
  • JVM在運行中過多的線程被創建,並且同時運行;
  • JVM爲線程分配的資源就可能耗盡native memory的容量;
  • JNI編程錯誤也可能導致native memory的內存泄漏;
    
 JNI編程錯誤造成的內存泄漏有以下幾類:

    1.Native Code本身造成的內存泄漏

  在JNI編程中,會使用到native編程語言,例如C/C++。native編程語言都自己的內存管理機制,如果沒有遵循native編程語言的內存管理機制,也會造成內存泄漏。例如,在C語言中,是通過malloc()函數分配內存,對應的釋放內存函數是free()。如果分配內存後,沒有調用相應的free()函數,則會造成native memory內存泄漏。

   2.Global Reference(全局引用)引入的內存泄漏

  JNI編程需要遵循JNI的規範標準,JVM附加了JNI編程特有的內存管理機制。在JNI中的Local Reference(局部引用)只在本地方法內有效,當本地方法返回時,會被自動失效。這種自動失效,會把它們所引用的Java對象中的引用計數減1,從而可以讓Java中的對象被回收,不會造成Java Heap中Java對象的內存泄漏。
  而JNI中的Global Reference(全局引用)對Java對象的引用一直有效,除非顯式的調用DeleteGlobalRef方法釋放全Global Reference。在使用Global Reference時,需要仔細維護對Global Reference的使用,在不使用的時候要及時釋放Global Reference,否則Global Reference引用的Java對象將永遠停留在Java Heap中,造成Java Heap的內存泄漏。

    3.Local Reference可能造成的內存泄漏

  雖然在本地方法返回時,JNI可以確保Local Reference引用的對象可以被自動回收。但還是存在以下幾種情況,需要手動釋放Local Reference,否則也會造成內存泄漏。
  1. 在本地方法中引用了很大的Java對象,在使用完該Java對象後,還需要執行一些耗時操作。如果不主動釋放該Local Reference的話,則將一直保持對Java對象的引用,需要等到本地方法返回時,才能釋放對Java對象的引用。在執行耗時操作期間,會由於Local Reference沒有及時釋放,導致Java Heap的內存泄漏。
  2. 在本地代碼中創建了大量的Local Reference對象,沒有及時手動釋放,導致內存溢出。此時需要手動釋放那些不再使用的Local Reference對象,例如在本地代碼中創建一個很大的對象數組。
  3. 不返回的本地函數。例如:在一個本地函數中循環處理消息,如果不釋放循環中使用的局部引用,則會無限地累積,進而導致內存泄漏。此時在循環體中需要手動釋放局部引用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章