java.lang.UnsatisfiedLinkError:Native Library XXX.so已经加载到另一个类加载器中

时间:2016-04-29 11:16:45

标签: java opencv tomcat java-native-interface

我已经部署了一个Web应用程序,其中包含以下代码。

System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);

现在,我部署了另一个也有相同代码的Web应用程序。当它试图加载库时,它会抛出以下错误。

Exception in thread "Thread-143" java.lang.UnsatisfiedLinkError: 
Native Library /usr/lib/jni/libopencv_java248.so already loaded in
another classloader

我想同时运行这两个应用程序。

到现在为止我已尝试过:

  1. 在一个应用程序中加载库并将上述异常捕获到另一个应用程序中
  2. 从两个应用程序中删除jar并将opencv.jar放入Tomcat的类路径中(即在/ usr / share / tomcat7 / lib中)。
  3. 但以上都没有,我可以做任何建议吗?

    选项二的

    编辑

    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    

    此行有效但在我实际使用该库时会出现异常。那是我跟随

    的时候
    Mat mat = Highgui.imread("/tmp/abc.png");
    

    我得到了这个例外

    java.lang.UnsatisfiedLinkError: org.opencv.highgui.Highgui.imread_1(Ljava/lang/String;)J
        at org.opencv.highgui.Highgui.imread_1(Native Method)
        at org.opencv.highgui.Highgui.imread(Highgui.java:362)
    

4 个答案:

答案 0 :(得分:10)

问题在于OpenCV如何处理本机库的初始化。

通常,使用本机库的类将具有加载库的静态初始化程序。这样,类和本机库将始终加载到同一个类加载器中。使用OpenCV,应用程序代码加载本机库。

现在存在一个限制,即只能在一个类加载器中加载本机库。 Web应用程序使用自己的类加载器,因此如果一个Web应用程序加载了本机库,则另一个Web应用程序不能执行相同操作。因此,加载本机库的代码不能放在webapp目录中,但必须放在容器的(Tomcat)共享目录中。如果您使用上面的常规模式编写了一个类(在使用类的静态初始化程序中为loadLibrary),那么将包含该类的jar放在共享目录中就足够了。但是,使用OpenCV和Web应用程序代码中的loadLibrary调用,本机库仍将被加载到错误的"类加载器,您将获得UnsatisfiedLinkError

使'#34;正确"类加载器加载本机库,您可以创建一个小类,只使用一个静态方法只执行loadLibrary。将此类放在一个额外的jar中,并将此jar放在共享的Tomcat目录中。然后在Web应用程序中,通过调用新的静态方法替换对System.loadLibrary的调用。这样,OpenCV类的类加载器及其本机库将匹配,并且可以初始化本机方法。

编辑:评论者要求的示例

而不是

public class WebApplicationClass {
    static {
        System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
    }
}

使用

public class ToolClassInSeparateJarInSharedDirectory {
    public static void loadNativeLibrary() {
        System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
    }
}

public class WebApplicationClass {
    static {
        ToolClassInSeparateJarInSharedDirectory.loadNativeLibrary();
    }
}

答案 1 :(得分:0)

从Tomcat版本9.0.138.5.357.0.92开始,我们添加了以下选项来解决此问题BZ-62830

1)使用JniLifecycleListener加载本机库。

例如要加载opencv_java343库,您可以使用:

<Listener className="org.apache.catalina.core.JniLifecycleListener"
          libraryName="opencv_java343" />

2)使用load()中的loadLibrary()org.apache.tomcat.jni.Library而不是System

例如

org.apache.tomcat.jni.Library.loadLibrary("opencv_java343");

使用这些选项中的任何一个都将使用Common ClassLoader加载本机库,因此所有Web应用程序都可以使用它。

答案 2 :(得分:0)

我陷入了这个确切的问题。

在Tomcat启动时,在Tomcat(v8.5.58)server.xml文件中添加侦听器似乎已成功加载dll文件(至少日志这样说),但是当您调用本机方法时,它将失败并显示java.lang .UnsatisfiedLinkError。

无论是否调用“ org.apache.tomcat.jni.Library.loadLibrary(“ TeighaJavaCore”);“在我的Java代码中没有区别,仍然存在相同的错误。我在项目中包含tomcat-jni依赖项,以启用“ org.apache.tomcat.jni.Library.loadLibrary(“ TeighaJavaCore”)“调用。虽然,但我猜没必要在Java代码中(在Web应用程序级别)调用“ org.apache.tomcat.jni.Library.loadLibrary(“ TeighaJavaCore”)“,因为在Tomcat启动时会自动加载TeighaJavaCore.dll(因为上面的侦听器是为此目的而在Tomcat容器级别定义的)

我还在这里检查“ org.apache.tomcat.jni.Library.loadLibrary”的源代码,它只是简单地调用了“ System.loadLibrary(libname)”。

https://github.com/apache/tomcat-native/blob/master/java/org/apache/tomcat/jni/Library.java

答案 3 :(得分:-1)

从javacpp&gt; = 1.3开始,您还可以更改war部署侦听器中的缓存文件夹(由系统属性定义):

System.setProperty("org.bytedeco.javacpp.cachedir",
                   Files.createTempDirectory( "javacppnew" ).toString());

请注意,本机库始终是解压缩的,并且会被多次加载(因为被视为不同的库)。