简体   繁体   English

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

[英]Java JNA: JRE crashes after application completes

I'm accessing a native lib with JNA. 我正在使用JNA访问本机库。 This is my first time ever working with JNA and I also don't have experience with c/c++. 这是我第一次使用JNA,而且我也没有使用c / c ++的经验。 I was able to get the application to work and I get a correct result returned and displayed using System.out.println() . 我能够使该应用程序正常工作,并且使用System.out.println()返回并显示了正确的结果。 This is the last line of my code. 这是我代码的最后一行。 The value is displayed in console and then Java crashes, eg. 该值显示在控制台中,然后Java崩溃,例如。 the dialog Java Platform SE binary has stopped working is displayed with option to close or debug. 将显示“ Java Platform SE binary has stopped working ”对话框, Java Platform SE binary has stopped working带有关闭或调试选项。

The console then shows this: 然后,控制台将显示以下内容:

Java Result: -1073741819

Windows Event viewer says 0xc0000005 which according to existing answers is a me Windows事件查看器说0xc0000005根据现有答案是我

EDIT 5 21th August 2018: 编辑5 2018年8月21日:

This has still not been solved and now I'm looking into it again. 这仍然没有解决,现在我再次进行调查。 What was solved was the issues with MemoryError when looping. 解决的是循环时MemoryError的问题。 Since I really need a solution now I also stop obfuscating the dll. 由于我现在确实需要解决方案,因此我也不再混淆dll。 AFAIK the supplier it out of business. AFAIK供应商将其停业。 They don't reply to questions and the web site is outdated. 他们不回答问题,该网站已过时。

My "documentation" comes from here 我的“文档”来自这里

Here the current java code: 这里是当前的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());
}

I'm kind of lost. 我有点迷路了。 Above works with an outdated version of the dll but not with the newest one. 以上适用于dll的过时版本,但不适用于最新版本的dll。 What's going wrong? 怎么了 Would disassembling it help in any way? 拆卸它有任何帮助吗?

OLD CODE and INFO , only partially relevant now: OLD CODE和INFO ,现在仅部分相关:

My code: 我的代码:

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
}

Defintion of the function in c: 在c中定义函数:

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

What is going wrong? 怎么了? Do I need to perform some cleanup before the application terminates? 我需要在应用程序终止之前执行一些清理吗?

EDIT: 编辑:

I can run the method call in a loop and it works. 我可以循环运行方法调用,并且可以正常工作。 It only ever crashed when it terminates. 它只有在终止时才崩溃。

EDIT2: 编辑2:

As per comment here code for MyDll: 根据这里的注释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);
}

Definition of the struct : struct定义:

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

EDIT 3: 编辑3:

Damn. 该死的。 I figured out that the available documentation is for an old version of the dll. 我发现可用的文档适用于dll的旧版本。 Using the old dll, everything works. 使用旧的dll,一切正常。 So now I need to get docs of new version from supplier. 因此,现在我需要从供应商处获取新版本的文档。

EDIT 4: 编辑4:

It works with old dll but the app consistently crashes after 65533 iterations (calls). 它适用于旧的dll,但该应用在65533次迭代(调用)后始终崩溃。 Each call uses exactly the same parameters. 每个调用使用完全相同的参数。

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)   

The whole point of the exercise is to be able to make thousands of calls quickly. 练习的重点是能够快速拨打数千个电话。

The Java result you get is an error message. 您得到的Java结果是一条错误消息。 -1073741819 is 0xc0000005, which is the code for STATUS_ACCESS_VIOLATION: you're accessing memory that you don't have permissions for. -1073741819是0xc0000005,这是STATUS_ACCESS_VIOLATION的代码:您正在访问没有权限的内存。

The fact that it's occurring when your application closes indicates that it's associated with object finalization, which occurs when your program terminates and could also occur when garbage collection is triggered. 它在应用程序关闭时发生的事实表明它与对象终结相关联,后者在程序终止时发生,也可能在触发垃圾回收时发生。 It is likely that Java/JNA is attempting to release memory that C has not allocated. Java / JNA可能试图释放C尚未分配的内存。

When you get this sort of error you should investigate: 当您遇到这种错误时,您应该调查:

  • Type mapping from C to JNA (particularly object size) 从C到JNA的类型映射(尤其是对象大小)
  • Memory allocation mismatches Java vs. C (which side is allocating the memory) 内存分配与Java与C不匹配(哪一侧正在分配内存)
  • Releasing resources/memory allocated on the C side by your API (handles, references, ports, etc.) 释放由API在C端分配的资源/内存(句柄,引用,端口等)

Type Mapping 类型映射

A comparison of your code vs. the c function definition shows two mismatches. 代码与c函数定义的比较显示出两个不匹配的地方。

  • The CALCRESULT function has a pointer to a long in C. Whether this is a 32-bit or 64-bit long is operating system dependent while Java's long is always 64-bit. CALCRESULT函数在C中有一个指向long的指针。这是32位还是64位long 取决于操作系统,而Java的long始终是64位。 You should map native long values to JNA's NativeLong type. 您应该将本机long值映射到JNA的NativeLong类型。 While this might not be causing the crash in this case, it is vitally important to know this distinction when the value is inside an array or a structure because it will throw off the byte offsets. 尽管在这种情况下这可能不会导致崩溃,但至关重要的是要知道值在数组或结构内部时的区别,因为它会抛出字节偏移。 As a pointer to a native long , your variable numContrib should therefore be of type NativeLongByReference 因此,作为指向本机long的指针,您的变量numContrib的类型应为NativeLongByReference

  • The CALCRESULT function has a pointer to the HANDLE type in C, but you pass a structure (by reference). CALCRESULT函数在C中有一个指向HANDLE类型的指针,但是您传递了一个结构(通过引用)。 A few notes here: 这里有一些注意事项:

    • I'm not sure why you have to use ByReference. 我不确定为什么您必须使用ByReference。 When you pass a structure as an argument like this, JNA turns it into the pointer for you, and takes care of allocating and releasing memory, and auto-reading the native memory into the structure for you. 当您将结构作为这样的参数传递时,JNA会将其变为您的指针,并负责分配和释放内存,并为您自动将本机内存读取到该结构中。 So in general you wouldn't pass a structure by reference. 因此,通常您不会通过引用传递结构。 (Personally, I like to avoid ByReference structures, and just manipulate a Pointer directly, creating a new Structure using that Pointer. Probably the same thing but it's more clear to me what's happening.) (就我个人而言,我喜欢避免使用ByReference结构,而是直接操作Pointer ,使用该Pointer创建一个新的Structure。可能是同一件事,但对我而言更清楚了发生了什么。)
    • The C method specifies a pointer the specific Windows type HANDLE . C方法指定特定Windows类型HANDLE的指针。 A Handle is a reference to an internally maintained table that has access to other system resources, etc. Handles should be released when you are done using them, or you tie up those resources. 句柄是对内部维护表的引用,该表可以访问其他系统资源等。在使用完句柄或占用这些资源后,应将其释放。 JNA models the HANDLE type; JNA对HANDLE类型进行建模; in this case you have a pointer so the appropriate type would probably be a HANDLEByReference . 在这种情况下,您有一个指针,因此适当的类型可能是HANDLEByReference

Memory allocation mismatches 内存分配不匹配

It's unclear why you are using a structure when the API provides you a pointer to a HANDLE type (JNA HANDLEByReference ). 目前尚不清楚,当API向您提供指向HANDLE类型的指针(JNA HANDLEByReference )时,为什么要使用结构。 Java allocates the necessary memory when you define a new instance of the structure (In your case the PContribution ) and JNA will map that Java memory to the local memory for you. 当您定义结构的new实例(在您的情况下为PContribution )时,Java会分配必要的内存,并且JNA会为您将Java内存映射到本地内存。 However, the C function is giving a pointer to the HANDLE type which takes up less memory than the structure you've defined. 但是,C函数提供了指向HANDLE类型的指针,该类型占用的内存少于您定义的结构。 It's entirely possible that when Java is freeing up the structure memory on the Java side and then trying to release the same memory on the C side, it's running into problems because it's telling C to release more memory (for the structure) than C allocated (for the HANDLE). 当Java在Java端释放结构内存,然后尝试在C端释放相同的内存时,这完全有可能会出现问题,因为它告诉C释放比结构C分配的更多的内存(用于结构)(的句柄)。 Answering further about this is not possible without more clear documentation from your API on how you get from this HANDLE to the structure you have listed in its place. 如果没有来自API的更清晰的文档说明如何从该HANDLE到已在其位置列出的结构,则无法进一步回答此问题。

Releasing resources 释放资源

Since the API is using the HANDLE type it's likely other resources than memory are involved, and you have a responsibility to release those references. 由于API使用的是HANDLE类型,因此很可能涉及内存以外的其他资源,您有责任释放这些引用。 See the CloseHandle() function. 请参见CloseHandle()函数。 Whether you have to close the handle yourself with this function, or whether the API will close it for you with one of its own methods implementing CloseHandle() internall should be clearly documented in the API (in this case for the CALCRESULT function). 是否必须使用此函数自己关闭该句柄,或者API是否使用自己的一种实现CloseHandle() internall的方法为您关闭它(在此情况下,对于CALCRESULT函数)。 Without the API to inspect I can't help much further, but carefully read the documentation to see if any of the structures you create in your code are required to be explicitly freed / released, and how. 没有API进行检查,我无济于事,但请仔细阅读文档,以了解是否需要显式释放/释放在代码中创建的任何结构,以及如何进行释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM