Java JNA:应用程序完成后JRE崩溃

时间:2016-06-07 11:42:10

标签: java jna jvm-crash

我正在使用JNA访问本机库。这是我第一次使用JNA,我也没有使用c / c ++的经验。我能够使应用程序工作,并使用System.out.println()返回并显示正确的结果。这是我的代码的最后一行。该值显示在控制台中,然后Java崩溃,例如。将显示对话框Java Platform SE binary has stopped working,其中包含关闭或调试选项。

然后控制台会显示:

Java Result: -1073741819

Windows事件查看器说0xc0000005根据现有答案是我

编辑5 2018年8月21日:

这仍然没有解决,现在我再次调查。解决的是循环时MemoryError的问题。因为我现在真的需要一个解决方案,所以我也停止混淆dll。 AFAIK供应商将其停业。他们不回复问题,网站已过时。

我的“文档”来自here

这里是当前的java代码:

public interface CLogP extends StdCallLibrary {
    CLogP INSTANCE = (CLogP) Native.loadLibrary("BB-CLOGP", CLogP.class);
    NativeLong calcLogP(String smiles, FloatByReference logP, NativeLongByReference numContrb, HANDLEByReference contrib);
}

public static void main(String[] args) {

    //contrib can also be a PointerByReference, the behaviour is the same
    HANDLEByReference contrib = new HANDLEByReference();
    FloatByReference cLogP = new FloatByReference();
    NativeLongByReference numContrib = new NativeLongByReference();
    NativeLong err = CLogP.INSTANCE.calcLogP("c1ccccc1", cLogP, numContrib, contrib);
    System.out.println(err.intValue());
    System.out.println(cLogP.getValue());
    System.out.println(numContrib.getValue());
    //needs to be done after ever call to calcLogP
    //then no issue running in a loop
    Kernel32.INSTANCE.GlobalFree(contrib.getValue().getPointer());
    // Tried to free other variables, does not have any effect
    //Kernel32.INSTANCE.GlobalFree(cLogP.getPointer());
    //Kernel32.INSTANCE.GlobalFree(numContrib.getPointer());
}

我有点迷茫。上面的内容适用于dll的过时版本,但不适用于最新版本。出了什么问题?拆解它会有什么帮助吗?

旧代码和信息,现在只是部分相关:

我的代码:

public static void main(String[] args) {

    FloatByReference result = new FloatByReference();
    //EDIT: changed to NativeLongByReference as per suggested answer
    NativeLongByReference numContrib = new NativeLongByReference();     
    // This is a struct that needs to be passed by reference
    MyDll.PContribution.ByReference contrib = new MyDll.PContribution.ByReference();        
    NativeLong err = MyDll.INSTANCE.calcResult("myValue", result, numContrib, contrib);
    // I only care about result and not the other out-parameters
    System.out.println(result.getValue());
    //crash here
}

c中函数的定义:

typedef long (CALCRESULT)(const char*, float*, long*, HANDLE*);

出了什么问题?在应用程序终止之前,我是否需要执行一些清理工作?

编辑:

我可以在循环中运行方法调用并且它可以工作。只有在它终止时它才会崩溃。

EDIT2:

根据评论此处代码为MyDll:

public interface MyDll extends Library {

    public static class PContribution extends Structure {
        public static class ByReference extends PContribution implements Structure.ByReference {

                public byte[] Class = new byte[10];
                public byte[] Type = new byte[6];
                public byte[] Description = new byte[40];
                public byte[] Comment = new byte[10];
                public float Value;
        }
            protected List<String> getFieldOrder() {
                    return Arrays.asList(new String[] { "Class", "Type", "Description", "Comment", "Value" });
                }
}

    MyDll INSTANCE = (MyDll) Native.loadLibrary("MyDll", MyDll.class);

    NativeLong calcResult(String smi, FloatByReference result, NativeLongByReference numContrb, PContribution contrib);
}

struct

的定义
typedef struct {
    char Class[10];
    char Type[6];
    char Description[40];
    char Comment[10];
    float Value;
} PContribution;

编辑3:

该死。我发现可用的文档是针对旧版本的dll。使用旧的DLL,一切正常。所以现在我需要从供应商那里获得新版本的文档。

编辑4:

它适用于旧的dll,但应用程序在65533次迭代(调用)后一直崩溃。每个调用都使用完全相同的参数。

java.lang.Error: Invalid memory access

at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:390)
at com.sun.jna.Function.invoke(Function.java:323)
at com.sun.jna.Library$Handler.invoke(Library.java:236)
at com.sun.proxy.$Proxy0.calcLogP(Unknown Source)   

练习的重点是能够快速拨打数千个电话。

1 个答案:

答案 0 :(得分:1)

您获得的Java结果是错误消息。 -1073741819是0xc0000005,这是STATUS_ACCESS_VIOLATION的代码:您正在访问您没有权限的内存。

当您的应用程序关闭时发生这一事实表明它与对象终结相关联,这在程序终止时发生,也可能在触发垃圾收集时发生。 Java / JNA可能正在尝试释放C尚未分配的内存。

当你遇到这种错误时,你应该调查:

  • 从C到JNA的类型映射(特别是对象大小)
  • 内存分配不匹配Java与C(哪一方正在分配内存)
  • 释放API(句柄,引用,端口等)在C端分配的资源/内存。

类型映射

您的代码与c函数定义的比较显示两个不匹配。

  • CALCRESULT函数有一个指向C中long的指针。这是一个32位还是64位长是operating system dependent而Java是long总是64位。您应该将原始long值映射到JNA的NativeLong类型。虽然在这种情况下这可能不会导致崩溃,但是当值在数组或结构中时,知道这种区别是非常重要的,因为它会抛弃字节偏移。作为指向原生long的指针,您的变量numContrib应为NativeLongByReference

  • 类型
  • CALCRESULT函数在C中有一个指向HANDLE类型的指针,但是你传递了一个结构(通过引用)。这里有几点说明:

    • 我不确定你为什么要使用ByReference。当您将结构作为参数传递时,JNA会将其转换为指针,并负责分配和释放内存,并自动将本机内存读入结构中。所以一般来说,你不会通过引用传递结构。 (就个人而言,我喜欢避免使用ByReference结构,只是直接操纵Pointer,使用指针创建一个新的结构。可能是同样的事情,但它对我来说更清楚发生了什么。 )
    • C方法指定特定Windows类型HANDLE的指针。 Handle是对内部维护的表的引用,该表可以访问其他系统资源等。当您使用它们时,应该释放句柄,或者占用这些资源。 JNA模拟HANDLE类型;在这种情况下,你有一个指针,所以适当的类型可能是HANDLEByReference

内存分配不匹配

当API为您提供指向HANDLE类型(JNA HANDLEByReference)的指针时,不清楚您使用结构的原因。当您定义结构的new实例(在您的情况下为PContribution)时,Java会分配必要的内存,JNA会将该Java内存映射到本地内存。但是,C函数给出一个指向HANDLE类型的指针,它占用的内存比你定义的结构少。完全有可能当Java在Java端释放结构内存然后尝试在C端释放相同的内存时,它会遇到问题,因为它告诉C到释放更多内存(用于结构)而不是C分配(用于HANDLE)。如果没有来自您的API的更清晰的文档,关于如何从此HANDLE到您在其所在位置列出的结构,则无法进一步回答这个问题。

发布资源

由于API使用HANDLE类型,因此可能涉及除内存之外的其他资源,并且您有责任发布这些引用。请参阅CloseHandle()功能。是否必须使用此函数自行关闭句柄,或者API是否将使用自己的方法(实现CloseHandle()内部函数)为您关闭它应该在API中明确记录(在本例中为CALCRESULT函数) 。如果没有要检查的API,我可以提供更多帮助,但请仔细阅读文档,看看是否需要明确释放/释放您在代码中创建的任何结构,以及如何。