1. 程式人生 > >在JNI程式設計中避免記憶體洩漏(二)

在JNI程式設計中避免記憶體洩漏(二)

JAVA 程式設計中的記憶體洩漏,從洩漏的記憶體位置角度可以分為兩種:JVM 中 Java Heap 的記憶體洩漏;JVM 記憶體中native memory 的記憶體洩漏。

Java 物件儲存在 JVM 程序空間中的 Java Heap 中,Java Heap 可以在 JVM 執行過程中動態變化。如果Java 物件越來越多,佔據 Java Heap 的空間也越來越大,JVM 會在執行時擴充 Java Heap 的容量。如果 JavaHeap 容量擴充到上限,並且在 GC 後仍然沒有足夠空間分配新的 Java 物件,便會丟擲 out of memory 異常,導致JVM 程序崩潰。

Java Heap 中 out of memory 異常的出現有兩種原因——①程式過於龐大,致使過多 Java物件的同時存在;②程式編寫的錯誤導致 Java Heap 記憶體洩漏。

多種原因可能導致 Java Heap 記憶體洩漏。JNI 程式設計錯誤也可能導致 Java Heap 的記憶體洩漏。

從作業系統角度看,JVM在執行時和其它程序沒有本質區別。在系統級別上,它們具有同樣的排程機制,同樣的記憶體分配方式,同樣的記憶體格局。

JVM 程序空間中,Java Heap 以外的記憶體空間稱為 JVM 的 native memory。程序的很多資源都是儲存在JVM 的 native memory 中,例如載入的程式碼映像,執行緒的堆疊,執行緒的管理控制塊,JVM 的靜態資料、全域性資料等等。也包括JNI 程式中 native code 分配到的資源。

在 JVM 執行中,多數程序資源從 native memory 中動態分配。當越來越多的資源在 native memory中分配,佔據越來越多 native memory 空間並且達到 native memory 上限時,JVM 會丟擲異常,使 JVM程序異常退出。而此時 Java Heap 往往還沒有達到上限。

多種原因可能導致 JVM 的 native memory 記憶體洩漏。例如 JVM在執行中過多的執行緒被建立,並且在同時執行。JVM 為執行緒分配的資源就可能耗盡 native memory 的容量。

JNI 程式設計錯誤也可能導致 native memory 的記憶體洩漏。對這個話題的討論是本文的重點。

JNI 程式設計實現了 native code 和 Java 程式的互動,因此 JNI 程式碼程式設計既遵循 native code程式語言的程式設計規則,同時也遵守 JNI 程式設計的文件規範。在記憶體管理方面,native code程式語言本身的記憶體管理機制依然要遵循,同時也要考慮 JNI 程式設計的記憶體管理。

本章簡單概括 JNI 程式設計中顯而易見的記憶體洩漏。從 native code 程式語言自身的記憶體管理,和 JNI規範附加的記憶體管理兩方面進行闡述。

JNI 程式設計首先是一門具體的程式語言,或者 C 語言,或者 C++,或者彙編,或者其它 native的程式語言。每門程式語言環境都實現了自身的記憶體管理機制。因此,JNI 程式開發者要遵循 native語言本身的記憶體管理機制,避免造成記憶體洩漏。以 C 語言為例,當用 malloc() 在程序堆中動態分配記憶體時,JNI程式在使用完後,應當呼叫 free() 將記憶體釋放。總之,所有在 native 語言程式設計中應當注意的記憶體洩漏規則,在 JNI程式設計中依然適應。

Native 語言本身引入的記憶體洩漏會造成 native memory 的記憶體,嚴重情況下會造成 native memory 的out of memory。

JNI 程式設計還要同時遵循 JNI 的規範標準,JVM 附加了 JNI 程式設計特有的記憶體管理機制。

JNI 中的 Local Reference 只在 native method 執行時存在,當 native method執行完後自動失效。這種自動失效,使得對 Local Reference 的使用相對簡單,native method執行完後,它們所引用的 Java 物件的 reference count 會相應減 1。不會造成 Java Heap 中 Java物件的記憶體洩漏。

而 Global Reference 對 Java 物件的引用一直有效,因此它們引用的 Java 物件會一直存在 JavaHeap 中。程式設計師在使用 Global Reference 時,需要仔細維護對 Global Reference的使用。如果一定要使用 Global Reference,務必確保在不用的時候刪除。就像在 C 語言中,呼叫 malloc()動態分配一塊記憶體之後,呼叫 free() 釋放一樣。否則,Global Reference 引用的 Java 物件將永遠停留在Java Heap 中,造成 Java Heap 的記憶體洩漏。