轉載-JAVA 關於JNI本地庫載入
轉載來源:https://www.cnblogs.com/tudachui/p/9729205.html
1.呼叫JNI的時候,通常我們使用System.loadLibrary(String libname)來load JNI library, 同樣也可以使用System.load(String fileName)來load JNI library,兩者的區別是一個只需要設定庫的名字,比如如果libA.so 只要輸入A就可以了,而libA.so的位置可以同過設定 java.library.path 或者 sun.boot.library.path,後者輸入的是完整路經的檔名。而不論用什麼方法,最後JNI 庫是通過classloader 來載入的
例:
(1)System.load 引數為庫檔案的絕對路徑,可以是任意路徑。
Java程式碼
(2)System.loadLibrary 引數為庫檔名,不包含庫檔案的副檔名
注意:這種方式,載入的dll檔案須是在java.library.path這一jvm變數所指向的路徑中。
可以通過如下方法來獲得該變數的值:
Linux一般預設的java.library.path在/usr/lib
下。
也可以自己通過VM引數-Djava.library.path=/usr/lib
來顯式的指定;或者通過增加環境變數export LD_LIBRARY_PATH=~/JavaNativeTest:$LD_LIBRARY_PATH
2.對於System.loadLibrary("NativeAgent");
在Linux下,動態庫輸出的檔名要是libNativeAgent.so
。
也就是說,如果System.loadLibrary("XXX")
;那麼,在匯出動態庫時,動態庫的名字就要是libXXX
。否則,會報錯:
3.每個classloader 物件都有自己的nativeLibrary 陣列,一個全域性的systemNativeLibrary 陣列,一個全域性的已經載入過的loadLibraryNames陣列,和一個正在載入過程中的記錄棧nativeLibraryContext
對同一個classloader 物件可以重複載入相同的庫,對不同的classloader只可以載入一次相同的庫
(1). 這裡定義的相同的庫是指相同路經下的同一個檔案
(2). 這裡同樣指出的是同一個classloader物件,而不是同一種classloader型別,比如說如果一種classloader型別初始化成2個classloader物件,那麼這兩個物件就不能重複載入相同的庫。
(3). 重複載入,並不代表真的重複載入,而是程式碼中保護
4.報錯處理:ava.lang.UnsatisfiedLinkError: Native Library kjdbc_jni already loaded in another classloader
原因:
1.java虛擬機器不允許一個JNI本地庫同時被兩個不同的classloader載入。
2.web伺服器自動重啟機制
當tomcat重啟web應用時會自動載入dll, 但是重啟web應用並不是重啟整個tomcat,上一次啟動的jvm仍然存在(jvm依然認為dll已經被之前的classloader載入過了),就不允許重啟後web應用的classloader再去載入它。
但是手動重啟tomcat,會將上一次啟動的jvm關閉並重新啟動,這樣就可以正常載入。
解決思路:
雖然不同的web應用使用不同的classloader,但是所有web應用classloader的父classloader是同一個(BootstrapClassLoader)
啟動類載入器BootstrapClassLoader:是嵌在JVM核心中的載入器,該載入器是用C++語言寫的,主要負載載入JAVA_HOME/lib下的類庫,啟動類載入器無法被應用程式直接使用。
根據雙親委託模型,只要讓父classloader載入jni本地庫就可避免被多個classloader載入
具體做法:
將載入dll的程式碼抽出來,單獨放到放到一個類裡,寫到一個static程式碼塊,(載入這個類時就會先執行static程式碼塊),然後將這個類變成一個jar檔案,放到tomcat\lib資料夾下。再在web.xml中加一個監聽,在專案啟動的時候就載入這個類,載入dll.
作者: 燃燒的竹子
出處: https://www.cnblogs.com/HittheRoad/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利.