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.

 

 

 

posted on 2018-09-30 13:32  兔大锤  阅读(4623)  评论(0编辑  收藏  举报

导航