Java loadlibrary分析及如何unload

来源:互联网 发布:铁岭金石网络会所 编辑:程序博客网 时间:2024/06/06 12:47

Java可以通过System.load 和 System.loadLibrary()加载动态库。

但是Java本身并没有提供unload的功能。

下面是在网上看到的一个load的原理及如何unload。地址:http://ppjava.com/?p=1273

Java加载JNI的动态库,有两种方式:

  • public static void load(String filename),从作为动态库的本地文件系统中以指定的文件名加载代码文件。文件名参数必须是完整的路径名。调用 System.load(name) 实际上等效于调用:Runtime.getRuntime().load(name)。
  • public static void loadLibrary(String libname),加载由 libname 参数指定的系统库。将库名映射到实际系统库的方法取决于系统。调用 System.loadLibrary(name) 实际上等效于调用:Runtime.getRuntime().loadLibrary(name)。

jni加载classpath中的动态链接库

1// 系统自己会判断扩展名是dll还是so
2System.loadLibrary("test");

具体方法请看这里→

jni加载jar包中的动态链接库

部署应用的时候要加载jar包中的动态链接库文件,可以将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到“C:\WINDOWS\System32”目录下了事,但这样需要用户做额外操作,有时候当前系统用户也没有权限拷贝库文件到指定目录。

有人可能会想到,在Java代码中利用System.setProPerty设置lib path,指向动态库文件所在路径。不过此法不可行,因为一旦Java虚拟机启动以后,lib path就是只读的,就不能再设置进去值了。

这个问题可以这样解决:

把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,

  1. 拷贝到classpath中,用System.loadLibrary(name)加载;
  2. 如果没有权限拷贝到指定目录,也可以拷贝到temp目录中,用System.load(path)加载。

1. 拷贝到classpath中,用System.loadLibrary(name)加载

1static {
2  InputStream in =null;
3  FileOutputStream out =null;
4  try{
5    String libpath = System.getProperty("java.library.path");
6    if(libpath == null || libpath.length() == 0)
7      thrownew RuntimeException("java.library.path is null");
8    String path =null;
9    String pathSeparator = System.getProperty("path.separator");
10    StringTokenizer st =new StringTokenizer(libpath, pathSeparator);
11    if(st.hasMoreElements())
12      path = st.nextToken();
13    else
14      thrownew RuntimeException("can not split library path : "+ libpath);
15    in = Foo.class.getResource("foo.dll").openStream();
16    File fooDll =new File(newFile(path), "foo.dll");
17    out = new FileOutputStream(fooDll);
18    byte[] buffer =new byte[2048];
19    intlen;
20    while((len = in.read(buffer)) != -1)
21      out.write(buffer,0, len);
22    out.close();
23    fooDll.deleteOnExit();
24    System.loadLibrary("foo");
25  } catch(Throwable e) {
26    e.printStackTrace();
27  } finally{
28    // 流的判空和关闭
29  }
30}

2. 拷贝到temp目录中,用System.load(path)加载

1static {
2  InputStream in =null;
3  FileOutputStream out =null;
4  try{
5    in = Foo.class.getResource("/foo.dll").openStream();
6    File temporaryDll = File.createTempFile("foo",".dll");
7    out = new FileOutputStream(temporaryDll);
8    byte[] buffer =new byte[2048];
9    intlen;
10    while((len = in.read(buffer)) != -1)
11      out.write(buffer,0, len);
12    out.close();
13    temporaryDll.deleteOnExit();
14    System.load(temporaryDll.getPath());
15  } catch(Throwable e) {
16    e.printStackTrace();
17  } finally{
18    // 流的判空和关闭
19  }
20}

为什么上面通过getResource取得了URL不直接去加载呢?因为如果把dll和class一起打成jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。ClassLoader中用new File(name),当然会找不到文件。

1URL url = Foo.class.getResource("/java/lang/String.class");
2System.out.println(url);
3System.out.println(url.toExternalForm());
4System.out.println(url.getFile());

以上代码输出结果如下:

jar:file:/E:/Java/jdk1.6.0_31/jre/lib/rt.jar!/java/lang/String.class
jar:file:/E:/Java/jdk1.6.0_31/jre/lib/rt.jar!/java/lang/String.class
file:/E:/Java/jdk1.6.0_31/jre/lib/rt.jar!/java/lang/String.class

jni卸载动态库文件

事实证明以上调用deleteOnExit()方法并不能在系统退出后删除动态库文件,由于程序占用而导致无法删除,所以要在程序退出时卸载动态库文件,这个样在程序退出时就可以删除动态临时创建的动态库文件了。我们在程序中加个hook,让程序退出时卸载动态链接库:

1Runtime.getRuntime().addShutdownHook(newThread() {
2  publicvoid run() {
3    // unloadAllNativeLibs();
4    unloadNativeLibs(temporaryDll.getName());
5  }
6});

那么如何在程序推出时卸载动态库文件呢?可以通过反射调用私有属性和私有方法来卸载:

1public staticsynchronized void unloadAllNativeLibs() {
2  try{
3    ClassLoader classLoader = Foo.class.getClassLoader();
4    Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
5    field.setAccessible(true);
6    Vector<Object> libs = (Vector<Object>) field.get(classLoader);
7    Iterator it = libs.iterator();
8    while(it.hasNext()) {
9      Object object = it.next();
10      Method finalize = object.getClass().getDeclaredMethod("finalize");
11      finalize.setAccessible(true);
12      finalize.invoke(object);
13    }
14  } catch(Throwable th) {
15    th.printStackTrace();
16  }
17}
18 
19public staticsynchronized void unloadNativeLibs(String libName) {
20  try{
21    ClassLoader classLoader = Foo.class.getClassLoader();
22    Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
23    field.setAccessible(true);
24    Vector<Object> libs = (Vector<Object>) field.get(classLoader);
25    Iterator it = libs.iterator();
26    while(it.hasNext()) {
27      Object object = it.next();
28      Field[] fs = object.getClass().getDeclaredFields();
29      for(int k = 0; k < fs.length; k++) {
30        if(fs[k].getName().equals("name")) {
31          fs[k].setAccessible(true);
32          String dllPath = fs[k].get(object).toString();
33          if(dllPath.endsWith(libName)) {
34            Method finalize = object.getClass().getDeclaredMethod("finalize");
35            finalize.setAccessible(true);
36            finalize.invoke(object);
37          }
38        }
39      }
40    }
41  } catch(Throwable th) {
42    th.printStackTrace();
43  }
44}

【注】unloadNativeLibs(String libName)这个 libName 不是那个“foo.dll”,而是一个“foo”+Long类型的随机数+“.dll”。


0 0
原创粉丝点击