java.lang.UnsatisfiedLinkError:本机库 XXX.so 已经加载到另一个类加载器中

2022-09-03 06:57:02

我已经部署了一个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. 从两个应用程序中删除了jars,并将opencv.jar放入Tomcat的classpath中(即在/usr/share/tomcat7/lib中)。

但是以上都没有奏效,任何建议我都可以做到这一点?

编辑:对于选项二,

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)

答案 1

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

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

现在有一个限制,即一个本机库只能装入一个类装入器中。Web 应用程序使用自己的类装入器,因此,如果一个 Web 应用程序装入了本机库,则另一个 Web 应用程序不能执行相同的操作。因此,代码加载本机库不能放在 webapp 目录中,而必须放在容器 (Tomcat) 的共享目录中。当你有一个用上面通常的模式编写的类(在使用类的静态初始值设定项中),将包含该类的jar放在共享目录中就足够了。但是,使用OpenCV和Web应用程序代码中的调用,本机库仍将加载到“错误”类加载器中,您将获得.loadLibraryloadLibraryUnsatisfiedLinkError

要使“正确的”类装入器装入本机库,您可以使用单个静态方法创建一个小类,仅执行 .将此类放在一个额外的 jar 中,并将此 jar 放在共享的 Tomcat 目录中。然后在 Web 应用程序中,将 对 的调用替换为对新静态方法的调用。这样,OpenCV 类的类装入器及其本机库将匹配,并且可以初始化本机方法。loadLibrarySystem.loadLibrary

编辑:评论者要求的示例

而不是

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();
    }
}

答案 2

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

1) 使用 JniLifecycleListener 加载本机库。

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

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

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

例如:

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

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


推荐