【问题标题】:JNA Native.loadLibrary generates a memory failure: __memmove_avx_unaligned_ermsJNA Native.loadLibrary 生成内存故障:__memmove_avx_unaligned_erms
【发布时间】:2018-12-06 01:27:52
【问题描述】:

我在 java 中开发了一个使用 JNA 加载 C++ 共享库的应用程序。详细过程如下。

  1. 在 jar 中查找库
  2. System.load( C_LIBRARY_PATH ) 与第 1 步的结果一起使用
  3. 使用 JNA 工具加载库:Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class );

此过程用于创建 c++ 库的包装器,从而生成用于其他项目的 java 库。第 1 点和第 2 点的灵感来自 adamheinrich 的工作。在调用load 函数之前检查文件是否存在。

在项目中使用 java 包装器时,我随机出现如下错误。能否指导我调试并尝试控制这种随机故障?

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007ff754e1cf9b, pid=8922, tid=9159
#
# JRE version: OpenJDK Runtime Environment (10.0.1+10) (build 10.0.1+10)
# Java VM: OpenJDK 64-Bit Server VM (10.0.1+10, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  [libc.so.6+0x15cf9b]  __memmove_avx_unaligned_erms+0x8b
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e" (or dumping to /home/core.8922)
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
---------------  S U M M A R Y ------------

Command Line: -Drebel.base=/home/.jrebel -Drebel.env.ide.plugin.version=2018.1.2 -Drebel.env.ide.version=2018.1 -Drebel.env.ide.product=IU -Drebel.env.ide=intellij -Drebel.notification.url=http://localhost:17434 -agentpath:/home/.IntelliJIdea2018.1/config/plugins/jr-ide-idea/lib/jrebel6/lib/libjrebel64.so -Dvisualvm.id=1716948864493 -Dmaven.multiModuleProjectDirectory=/home/ -Dmaven.home=/home/Documents/ideaIU-2018.1/idea-IU-181.4203.550/plugins/maven/lib/maven3 -Dclassworlds.conf=/home/Documents/ideaIU-2018.1/idea-IU-181.4203.550/plugins/maven/lib/maven3/bin/m2.conf -javaagent:/home/ideaIU-2018.1/idea-IU-181.4203.550/lib/idea_rt.jar=36953:/home/ideaIU-2018.1/idea-IU-181.4203.550/bin -Dfile.encoding=UTF-8 org.codehaus.classworlds.Launcher -Didea.version=2018.1 -P tomcat clean tomcat7:run -f pom.xml

Host: Intel(R) Core(TM) i7-6560U CPU @ 2.20GHz, 4 cores, 15G, Fedora release 28 (Twenty Eight)
Time: Wed Jun 27 08:35:16 2018 CEST elapsed time: 140 seconds (0d 0h 2m 20s)

---------------  T H R E A D  ---------------

Current thread (0x00007ff74efc1000):  JavaThread "http-bio-8080-exec-7" daemon [_thread_in_native, id=9159, stack(0x00007ff694588000,0x00007ff694689000)]

Stack: [0x00007ff694588000,0x00007ff694689000],  sp=0x00007ff694683ae8,  free space=1006k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libc.so.6+0x15cf9b]  __memmove_avx_unaligned_erms+0x8b

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.lang.ClassLoader$NativeLibrary.load0(Ljava/lang/String;Z)Z+0 java.base@10.0.1
j  java.lang.ClassLoader$NativeLibrary.load()Z+63 java.base@10.0.1
j  java.lang.ClassLoader$NativeLibrary.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)Z+239 java.base@10.0.1
j  java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+46 java.base@10.0.1
j  java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+50 java.base@10.0.1
j  java.lang.Runtime.load0(Ljava/lang/Class;Ljava/lang/String;)V+77 java.base@10.0.1
j  java.lang.System.load(Ljava/lang/String;)V+7 java.base@10.0.1
j  *hiddenPackageName*.NativeUtils.loadLibraryFromJar(Ljava/lang/String;Ljava/lang/Class;)V+359
j  *hiddenPackageName*.LibraryInitializer.<clinit>()V+13
v  ~StubRoutines::call_stub

为了全面了解问题,我复制了以下程序第 1 点和第 2 点的代码:

public static <T> void loadLibraryFromJar(String path, Class<T> clazz) throws IOException {
    if( !path.startsWith( "/" ) ){
        throw new IllegalArgumentException( "The path has to be absolute (start with '/')." );
    }

    // Obtain filename from path
    String[] parts = path.split( "/" );
    String filename = (parts.length > 1) ? parts[parts.length - 1] : null;

    // Split filename to prefix and suffix (extension)
    String prefix = "";
    String suffix = null;
    if( filename != null ){
        parts = filename.split( "\\.", 2 );
        prefix = parts[0];
        suffix = (parts.length > 1) ? "." + parts[parts.length - 1] : null; // Thanks, davs! :-)
    }

    // Check if the filename is okay
    if( filename == null || prefix.length() < 3 ){
        throw new IllegalArgumentException( "The filename has to be at least 3 characters long." );
    }

    // Prepare temporary file
    File temp = File.createTempFile( prefix, suffix );
    temp.deleteOnExit();

    if( !temp.exists() ){
        throw new FileNotFoundException( "File " + temp.getAbsolutePath() + " does not exist." );
    }

    // Prepare buffer for data copying
    byte[] buffer = new byte[1024];
    int readBytes;

    // Open and check input stream
    try( InputStream is = clazz.getResourceAsStream( path ) ){
        if( is == null ){
            throw new FileNotFoundException( "File " + path + " was not found inside JAR." );
        }

        // Open output stream and copy data between source file in JAR and the temporary file
        try( OutputStream os = new FileOutputStream( temp ) ){
            while( (readBytes = is.read( buffer )) != -1 ){
                os.write( buffer, 0, readBytes );
            }
        }
    }

    // Finally, load the library
    System.load( temp.getAbsolutePath() );
}

编辑: temp.getAbsolutePath() 的值随后等于:

/tmp/libgcc_s7394566251608466486.so.1

/tmp/libgomp14105897271936182307.so.1

/tmp/libpthread18366551914953688272.so.0

/tmp/libstdc++3062153806840733749.so.6

/tmp/libfitting1621020429668741777.so.1.0

【问题讨论】:

  • __memcpy_sse2_unaligned - what does this mean in detail? - 表明memcpy 出现问题。可能是 JITer 发出的本机代码在 System.load 调用中遇到空指针。
  • 传递给System.load()temp.getAbsolutePath()的值是多少?
  • @cubrr:我已编辑问题以包含您的请求。请注意,我现在认为错误来自流程的第 3 步:Native.loadLibrary。
  • 我猜INSTANCEWrapper 接口内的隐式静态变量。这个Native.loadLibrary 调用是在第一次引用Wrapper 类时进行的(我相信)。我想知道System.load 调用是否在与类初始化不同的线程上完成?如果是这样,可能存在竞争条件 - JNA 是否会在库仍在写入或加载时尝试加载它?不要使用单例静态INSTANCE,而是在需要时尝试手动加载实例并传递该实例。然后看看错误是否仍然存在。
  • @cubrr:你猜对了,它是接口内部的隐式静态变量。改变这是一项巨大的工作,因为整个概念是这个实例可用于类的所有实例化。这非常有用。直到我们在网络应用程序中使用这个库之前,它从未被证明是一个问题……我会想办法检查是否存在线程竞争。并随时通知您。非常感谢。

标签: java shared-libraries jna


【解决方案1】:

我不小心在多个 java 类中加载了两次相同的库。更准确地说,调用Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 是在一个嵌套接口中完成的,我在所有使用相同c++ 库的不同对象中复制了该接口。正如@cubrr 所提醒的,这是在第一个对象的实例化时静态完成的。因此,该库被多次加载。下面是说明我的错误的伪代码。

class A {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionA();
    }
    public double callA() {
        return Wrapper.INSTANCE.functionA();
    }
}

class B {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionB();
    }
    public double callB() {
        return Wrapper.INSTANCE.functionB();
    }
}

应该这样做

class Parent {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionA();
        double functionB();
    }
}

class A extends Parent {
    public double callA() {
        return Wrapper.INSTANCE.functionA();
    }
}

class B extends Parent {
    public double callB() {
        return Wrapper.INSTANCE.functionB();
    }
}

【讨论】:

  • 哇,这很有趣。我会认为 JNA 会尝试重新加载已加载的库。很高兴听到我有一些用处!
  • 顺便说一句,请接受您的回答,因为它提供并回答了您的问题!
  • 我必须再等 16 个小时才能这样做 :-)
  • 这似乎有点令人费解。我希望您定义本地库映射的单个“全局”实例,然后在每个类中引用该实例。我将实例化隐藏在一个类中的唯一原因是,如果我想要单独的库实例化(例如,我想要多线程运行的具有共享数据块的 fortran 代码)。
  • 我希望 java 用户不要打扰(甚至不知道)他正在使用的类后面有一个 c++ 库。我认为这会奏效。如果您有一个传递的全局属性,那么它会无缘无故地超载使用。 A 类是 c++ 库子部分的包装器。 B 类是 c++ 库的另一个子部分的包装器。用户无需管理本机代码。它是自动的。就像 python 使用 c++ 编译的代码一样,我们没有注意到......让我知道你的想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多