JAVA 關於JNI本地庫加載


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.

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM