代码之家  ›  专栏  ›  技术社区  ›  Fred

java.lang.UnsatisfiedLinkError,即使库和类具有方法

  •  1
  • Fred  · 技术社区  · 5 年前

    我使用Python和Py4J来测试JNI代码。但是,当我调用JNI代码时,会得到以下错误:

    py4j.protocol.Py4JJavaError: An error occurred while calling o37.createInstance.
    : java.lang.UnsatisfiedLinkError: com.mgr_api_JNI.createInstance(Lcom/mgr_api_types$EDisplayType;Ljava/lang/String;Lcom/mgr_api_types$ECommType;Ljava/lang/String;)V
        at com.mgr_api_JNI.createInstance(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
        at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
        at py4j.Gateway.invoke(Gateway.java:282)
        at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
        at py4j.commands.CallCommand.execute(CallCommand.java:79)
        at py4j.GatewayConnection.run(GatewayConnection.java:238)
        at java.base/java.lang.Thread.run(Thread.java:834)
    

    link 1 , link 2 link 3 , link 4 , link 5 ,和 link 6 ,再加上其他的,但没有一个能解决我的问题。

    代码

    api_JNI.java经理:

    package com;
    import com.mgr_api_types.*;
    
    public class mgr_api_JNI
    {
        static
        {
            try
            {
                System.loadLibrary("Mngr"); // Use "-Djava.library.path=[path to library]" option to load this library
            }
            catch (UnsatisfiedLinkError e)
            {
                System.err.println("Native code library 'Mngr' failed to load.\n" + e);
            }
        }
    
        public native void createInstance(com.mgr_api_types.EDisplayType displayType,
                                          String displaySerialNumber,
                                          com.mgr_api_types.ECommType commType,
                                          String portName);
    }
    

    import com.*;
    import py4j.GatewayServer;
    
    public class testsJNI
    {
        public static void main(String args[])
        {
            testsJNI testApp = new testsJNI();
    
            // Py4J server
            GatewayServer server = new GatewayServer(testApp);
            server.turnLoggingOff();
            server.start();
        }
    }
    

    com_mgr_api_JNI.h公司

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_mgr_api_JNI */
    
    #ifndef _Included_com_mgr_api_JNI
    #define _Included_com_mgr_api_JNI
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_mgr_api_JNI
     * Method:    createInstance
     * Signature: (Lcom/mgr_api_types/EDisplayType;Ljava/lang/String;Lcom/mgr_api_types/ECommType;Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_com_mgr_1api_1JNI_createInstance
    (JNIEnv *, jobject, jobject, jstring, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    com_mgr_api_JNI.cpp公司:

    #include "com_mgr_api_JNI.h"
    static manager::CManagerApi* manager = NULL;
    
    JNIEXPORT void JNICALL Java_com_mgr_1api_1JNI_createInstance(
                                JNIEnv *env,
                                jobject thisObj, 
                                jobject displayType,
                                jstring displaySerialNumber,
                                jobject commType,
                                jstring portName)
    {
      manager::EDisplayType dType = convertObjectToDisplayType(env, displayType);
      const char* serialNumber = env->GetStringUTFChars(displaySerialNumber, 0);
      manager::ECommType comm = convertObjectToCommType(env, commType);
      const char* port = env->GetStringUTFChars(portName, 0);
    
      char buf[100];
      sprintf(buf,"%s",port);
      std::string portStr = buf;
    
      sprintf(buf,"%s",serialNumber);
      std::string serialNumStr = buf;
    
      if (manager == NULL)
      {
        manager = manager::CManagerApi::get();
        manager->initialize(dType, serialNumStr, comm, portStr);
      }
    
      // Release memory
      env->ReleaseStringUTFChars(displaySerialNumber, serialNumber);
      env->ReleaseStringUTFChars(portName, port);
    }
    

    Java代码的命令行执行:

    java -cp /mnt/c/Workspace/library/java/:.:/home/fred/.local/share/py4j/py4j0.10.7.jar -Djava.library.path=/mnt/c/Workspace/build/library/ testsJNI
    

    做一个 -XshowSettings:properties 显示以下属性:

    awt.toolkit = sun.awt.X11.XToolkit
    file.encoding = UTF-8
    file.separator = /
    java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
    java.awt.printerjob = sun.print.PSPrinterJob
    java.class.path = /mnt/c/Workspace/library/java/
        .
        /home/fred/.local/share/py4j/py4j0.10.7.jar
    java.class.version = 55.0
    java.home = /usr/lib/jvm/java-11-openjdk-amd64
    java.io.tmpdir = /tmp
    java.library.path = /mnt/c/Workspace/build/library/
    

    试图解决问题

    确保我有一个有效的本地库

    做一个 ls /mnt/c/Workspace/build/library/ 显示库 libMngr.so .

    如果我删除 李布尼格·索 从那个位置,然后试着运行Java抱怨它找不到库。

    nm 命令打开 李布尼格·索 显示以下内容:

    000000000021f269 T Java_com_mgr_1api_1JNI_createInstance
    

    objdump 命令:

    $objdump -f build/library/libMngr.so
    
    build/library/libMngr.so:     file format elf64-x86-64
    architecture: i386:x86-64, flags 0x00000150:
    HAS_SYMS, DYNAMIC, D_PAGED
    start address 0x000000000018aee0
    

    显示本机库是64位的,并且根据 -XshowSettings:属性 我正在使用64位Java。

    System.loadLibrary("Mngr"); 在里面 mgr_api_JNI.java 以确保只加载一次本机库。

    更新

    api_JNI.java经理 并将函数声明复制到 .cpp

    确保我有有效的Java类

    如果我做一个 负载感应 在java.class.path上 /mnt/c/Workspace/library/java/ 我从编译中找到了所有的Java类 api_JNI.java经理 .

    如果我删除这些类并尝试运行Java,那么Java会抱怨它找不到这些类。

    做一个 grep -r createInstance 在java.class.path上 返回:

    Binary file com/mgr_api_JNI.class matches
    com/mgr_api_JNI.java: public native void createInstance(com.mgr_api_types.EDisplayType displayType,
    

    所以Java可以找到编译后的Java类 createInstance 方法。

    问题

    在我看来,Java可以找到所有需要的类和本机库,但我仍然得到 UnsatisfiedLinkError 错误。

    为什么我还是会犯这个错误?

    我需要做什么来帮助Java找到/识别 创建实例 方法?

    1 回复  |  直到 5 年前
        1
  •  1
  •   Oo.oO    5 年前

    这是你的错误

    /mnt/c/Workspace/build/library/libMgr.so 但是你在代码中加载这个: System.loadLibrary("Mngr"); -你有个错别字

    如果名称正确,您可以确保它按预期工作

    当你“离开”首字母时 JVM , -Djava.library.path 不再是向其他JVM提供信息的有效位置。我不知道 py4j.GatewayConnection ,但最好别再跑了 虚拟机 你不使用的实例或 JNI

    我建议进行以下测试:

    • 设置 LD_LIBRARY_PATH 到lib所在的位置

    • 确保只使用 public class mgr_api_JNI (不带Python网桥引擎)

    • 如果有可能您的第二个JVM正在加载本机代码(但找不到),请尝试使用 _JAVA_OPTIONS=-Djava.library.path=[path to library] JVMs 会“看到”位置-但你应该这样做只是为了测试

    从另一个进程加载lib显然有问题。如果我把你的代码简化成这样:

    .
    |-- Makefile
    |-- README.md
    |-- c
    |   `-- com_mgr_api_JNI.cc
    |-- java
    |   `-- com
    |       |-- mgr_api_JNI.java
    |       `-- mgr_api_types
    |           |-- ECommType.java
    |           `-- EDisplayType.java
    |-- lib
    `-- target
    

    以及代码本身

    #include <iostream>
    #include "jni.h"
    #include "com_mgr_api_JNI.h"
    
    using namespace std;
    
    JNIEXPORT void JNICALL Java_com_mgr_1api_1JNI_createInstance
      (JNIEnv *env, jobject cls, jobject a, jstring b, jobject c, jstring d) {
      cout << "Hello";
    }
    

    java/com/mgr_api_JNI.java

    package com;
    import com.mgr_api_types.*;
    
    public class mgr_api_JNI
    {
      static {
        try {
          System.loadLibrary("Mngr"); // Use "-Djava.library.path=[path to library]" option to load this library
        } catch (UnsatisfiedLinkError e) {
          System.err.println("Native code library 'Mngr' failed to load.\n" + e);
        }
      }
    
      public native void createInstance(  com.mgr_api_types.EDisplayType displayType,
                                          String displaySerialNumber,
                                          com.mgr_api_types.ECommType commType,
                                          String portName);
    
      public static void main(String [] arg) {
    
        mgr_api_JNI obj = new mgr_api_JNI();
        obj.createInstance(new com.mgr_api_types.EDisplayType(), "", new com.mgr_api_types.ECommType(), "");
    
      }
    }
    

    java/com/mgr_api_types/ECommType.java

    package com.mgr_api_types;
    
    public class ECommType { }
    

    猫java/com/mgr_api_types/EDisplayType.java

    package com.mgr_api_types;
    
    public class EDisplayType { }
    

    生成文件.common

    ARCH=$(shell uname -s | tr '[:upper:]' '[:lower:]')
    ifeq ($(ARCH),darwin)
      EXT=dylib
    else
      EXT=so
    endif
    

    生成文件

    include Makefile.common
    
    all: compilejava compilec
    
    compilec:
        c++ -std=c++11 -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(ARCH) c/com_mgr_api_JNI.cc -o lib/libMngr.$(EXT)
    
    compilejava:
        $(JAVA_HOME)/bin/javac -h c -d target -cp target java/com/mgr_api_types/EDisplayType.java
        $(JAVA_HOME)/bin/javac -h c -d target -cp target java/com/mgr_api_types/ECommType.java
        $(JAVA_HOME)/bin/javac -h c -d target -cp target java/com/mgr_api_JNI.java
    
    test:
        $(JAVA_HOME)/bin/java -Djava.library.path=$(LD_LIBRARY_PATH):./lib -cp target com.mgr_api_JNI
    
    clean:
        -rm -rfv target/*
        -rm c/*.h
        -rm -rf lib/*
    

    make test
    /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -Djava.library.path=:./lib -cp target com.mgr_api_JNI
    Hello
    

    我认为您的类是从另一个JVM实例或通过JNI调用的对象调用的,或者是从另一个进程和 不见了。

    同时,尝试使用 System.load("full/path/of/library.so") 虚拟机 .

        2
  •  0
  •   Jorn Vernee    5 年前

    当运行库无法在共享库中加载相应的本机函数时,会出现此问题。

    因此,在这些情况下,最好重新生成头文件,看看函数名是否正确。

    在这种情况下,你的函数名看起来可疑

    Java_com_1mgr_1api_1JNI_createInstance
    

    _1 在Java类/函数名中编码文字下划线。所以,基于类名 mgr_api_JNI 名字应该是

    Java_com_mgr_1api_1JNI_createInstance
    

    _1个 只是一个简单的下划线 _