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.