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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36936948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 18:51:18  来源:igfitidea点击:

java.lang.UnsatisfiedLinkError: Native Library XXX.so already loaded in another classloader

javaopencvtomcatjava-native-interface

提问by Bhushan

I have deployed one web-application, which contains following code.

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

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

Now, I deployed another web-application which also have same code. When it tries to load library, it throwing following error.

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

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

I want to run these both application simultaneously.

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

Till now what I have tried:

到目前为止,我已经尝试过:

  1. Loaded library in one application and caught above exception into another application
  2. Removed jars from both application and put opencv.jar into Tomcat's classpath(ie in /usr/share/tomcat7/lib).
  1. 在一个应用程序中加载库并在另一个应用程序中捕获上述异常
  2. 从两个应用程序中删除 jars 并将 opencv.jar 放入 Tomcat 的类路径(即在 /usr/share/tomcat7/lib 中)。

But none of above worked, any suggestions by which I can do this ?

但以上都没有奏效,有什么建议可以让我做到这一点吗?

Edit:for option two,

编辑:对于选项二,

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

This line works but gets exception when I am actually going to use that library. That is when I do following

此行有效,但当我实际要使用该库时会出现异常。那是当我做以下

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

And I get this exception

我得到这个例外

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)

采纳答案by user2543253

The problem is with how OpenCV handles the initialization of the native library.

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

Usually a class that uses a native library will have a static initializer that loads the library. This way the class and the native library will always be loaded in the same class loader. With OpenCV the application code loads the native library.

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

Now there's the restriction that a native library can only be loaded in one class loader. Web applications use their own class loader so if one web application has loaded a native library, another web application cannot do the same. Therefore code loading native libraries cannot be put in a webapp directory but must be put in the container's (Tomcat) shared directory. When you have a class written with the usual pattern above (loadLibraryin static initializer of using class) it's enough to put the jar containing the class in the shared directory. With OpenCV and the loadLibrarycall in the web application code however, the native library will still be loaded in the "wrong" class loader and you will get the UnsatisfiedLinkError.

现在有一个限制,即只能在一个类加载器中加载本机库。Web 应用程序使用它们自己的类加载器,因此如果一个 Web 应用程序加载了本机库,另一个 Web 应用程序就不能这样做。因此加载本机库的代码不能放在 webapp 目录中,而必须放在容器(Tomcat)共享目录中。当你有一个用上面通常的模式编写的类(loadLibrary在 using class 的静态初始化程序中)时,将包含该类的 jar 放在共享目录中就足够了。loadLibrary但是,使用 OpenCV 和Web 应用程序代码中的调用,本机库仍将加载到“错误”的类加载器中,您将获得UnsatisfiedLinkError.

To make the "right" class loader load the native library you could create a tiny class with a single static method doing only the loadLibrary. Put this class in an extra jar and put this jar in the shared Tomcat directory. Then in the web applications replace the call to System.loadLibrarywith a call to your new static method. This way the class loaders for the OpenCV classes and their native library will match and the native methods can be initialized.

为了使“正确”的类加载器加载本机库,您可以创建一个带有单个静态方法的小类,该类只执行loadLibrary. 将这个类放在一个额外的jar 中,并将这个jar 放在共享的Tomcat 目录中。然后在 Web 应用程序中,将调用替换System.loadLibrary为对新静态方法的调用。通过这种方式,OpenCV 类的类加载器和它们的本机库将匹配,并且可以初始化本机方法。

Edit: example as requested by a commenter

编辑:评论者要求的示例

instead of

代替

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

use

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

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

回答by maison

As of javacpp>=1.3 you may also change the cache folder (defined by system property) in your war deployment listener:

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

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

Note though that native libraries are always unpacked and will be loaded several times (because considered as different libs).

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

回答by isapir

As of Tomcat versions 9.0.13, 8.5.35, and 7.0.92we have added the following options to address this issue BZ-62830:

由于Tomcat版本内容9.0.138.5.35以及7.0.92我们增加了下列选项来解决这一问题BZ-62830

1) Use the JniLifecycleListenerto load the native library.

1) 使用JniLifecycleListener加载本机库。

e.g. to load the opencv_java343library, you can use:

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

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

2) Use the load()or loadLibrary()from org.apache.tomcat.jni.Libraryinstead of System.

2) 使用load()loadLibrary()fromorg.apache.tomcat.jni.Library代替System

e.g.

例如

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

Using either of those options will use the Common ClassLoader to load the native library, and therefore it will be available to all of the Web Apps.

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