有没有办法记录 JNA 中无效内存访问的原因
Is there a way to log the reason of an Invalid memory access in JNA
使用 JNA 从 C 调用 Java 代码时出现以下错误:
Native Exception in org.InvokerPackageCallback@3ba987b8
java.lang.Error: Invalid memory access
at com.sun.jna.Native.getStringBytes(Native Method)
at com.sun.jna.Native.getString(Native.java:2248)
at com.sun.jna.Pointer.getString(Pointer.java:681)
at com.sun.jna.Structure.readField(Structure.java:743)
at com.sun.jna.Structure.read(Structure.java:605)
at org.PackageStruct$ByReference.read(PackageStruct.java:102)
at com.sun.jna.Structure.autoRead(Structure.java:2234)
at com.sun.jna.Structure.conditionalAutoRead(Structure.java:576)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.convertArgument(CallbackReference.java:650)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback(CallbackReference.java:571)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.callback(CallbackReference.java:610)
at com.sun.jna.Native.invokeVoid(Native Method)
at com.sun.jna.Function.invoke(Function.java:415)
at com.sun.jna.Function.invoke(Function.java:361)
at com.sun.jna.Library$Handler.invoke(Library.java:265)
在我的用例中,C 代码调用映射到 Java 接口方法的 publish_package_fct_pt
函数。此方法只有一个结构参数:
void publish_package_fct_ptr(void (*pf) (PackageStruct));
我使用以下库:
public interface MyLib extends Library {
public void publish_package_fct_ptr(InvokerPackage callback);
public interface InvokerPackage extends Callback {
void invoke(PackageStruct.ByReference pack);
}
}
具有以下结构定义:
public class PackageStruct extends Structure {
public NativeLong id;
public int type;
public int subtype;
public String configuration;
public String name;
public NativeLong date;
public PackageStruct(Pointer peer) {
super(peer);
}
public PackageStruct() {
super();
}
protected List<String> getFieldOrder() {
return Arrays.asList("id", "type", "subtype", "configuration", "name", "date");
}
public static class ByReference extends PackageStruct implements Structure.ByReference {
public void read() {
super.read();
}
}
C 结构是:
struct PackageStruct {
long id;
int type;
int subtype;
const char *configuration;
const char *name;
long date;
};
在我添加 configuration
字段之前它工作正常,现在我遇到了异常。我“认为”configuration
字段没有指向任何内容,这可能是我出现此错误的原因。
你同意我的假设吗?在 JNA 中是否有一种方法可以记录 com.sun.jna.Structure.readField
以了解我在哪个字段中出现此错误?
JNA 抛出的“无效内存访问”错误是对 Structured Exception Handling 捕获的本机内存错误的“优雅”处理。所以要回答标题中的问题,不幸的是你无法从原生方面获得更多细节。
您真正需要知道的是,要么您正试图访问您不拥有的本机内存,要么您的本机内存分配失败.
您的映射看起来正确,但您没有显示实际调用 publish_package_fct_ptr()
方法的代码。这可能就是问题所在。
但是,它是一个回调这一事实指向了 JNA 和回调的一个非常普遍的问题,其中内存在您使用它之前被释放,所以我猜测这就是您在这里遇到的问题。
JNA 使用 Memory
class 跟踪其内存分配。如果您在构造它时没有为结构提供指针,它将分配必要的内存并将其内部存储在 Memory
object 中。这适用于结构的大多数用途。
但是,释放本机内存与 Java 的垃圾相关 collection。当 Structure 被 GC 时,其内部内存 object 也会被 GC,并且作为其 finalize()
执行的一部分,它会释放本机内存。
如果回调是您最后一次引用 Java 中的结构,它将变得不可访问并且有资格被 GC。在某些情况下,这可能会在回调完成执行之前发生,从而取消本机内存分配。
如果您使用的是 JDK 9+,在回调后使用 ReachabilityFence
将解决此问题。对于 JDK 8 及更早版本,您可能必须在回调中使用结构后对其进行一些其他代码操作,以确保 Java 保留对它的引用(及其底层本机内存)。
(Aside/unrelated 问题:如果用作方法参数,则不需要声明 Structure ByReference。这是默认设置。您可能还会发现 @FieldOrder
注释比覆盖 getFieldOrder()
方法。)
使用 JNA 从 C 调用 Java 代码时出现以下错误:
Native Exception in org.InvokerPackageCallback@3ba987b8
java.lang.Error: Invalid memory access
at com.sun.jna.Native.getStringBytes(Native Method)
at com.sun.jna.Native.getString(Native.java:2248)
at com.sun.jna.Pointer.getString(Pointer.java:681)
at com.sun.jna.Structure.readField(Structure.java:743)
at com.sun.jna.Structure.read(Structure.java:605)
at org.PackageStruct$ByReference.read(PackageStruct.java:102)
at com.sun.jna.Structure.autoRead(Structure.java:2234)
at com.sun.jna.Structure.conditionalAutoRead(Structure.java:576)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.convertArgument(CallbackReference.java:650)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback(CallbackReference.java:571)
at com.sun.jna.CallbackReference$DefaultCallbackProxy.callback(CallbackReference.java:610)
at com.sun.jna.Native.invokeVoid(Native Method)
at com.sun.jna.Function.invoke(Function.java:415)
at com.sun.jna.Function.invoke(Function.java:361)
at com.sun.jna.Library$Handler.invoke(Library.java:265)
在我的用例中,C 代码调用映射到 Java 接口方法的 publish_package_fct_pt
函数。此方法只有一个结构参数:
void publish_package_fct_ptr(void (*pf) (PackageStruct));
我使用以下库:
public interface MyLib extends Library {
public void publish_package_fct_ptr(InvokerPackage callback);
public interface InvokerPackage extends Callback {
void invoke(PackageStruct.ByReference pack);
}
}
具有以下结构定义:
public class PackageStruct extends Structure {
public NativeLong id;
public int type;
public int subtype;
public String configuration;
public String name;
public NativeLong date;
public PackageStruct(Pointer peer) {
super(peer);
}
public PackageStruct() {
super();
}
protected List<String> getFieldOrder() {
return Arrays.asList("id", "type", "subtype", "configuration", "name", "date");
}
public static class ByReference extends PackageStruct implements Structure.ByReference {
public void read() {
super.read();
}
}
C 结构是:
struct PackageStruct {
long id;
int type;
int subtype;
const char *configuration;
const char *name;
long date;
};
在我添加 configuration
字段之前它工作正常,现在我遇到了异常。我“认为”configuration
字段没有指向任何内容,这可能是我出现此错误的原因。
你同意我的假设吗?在 JNA 中是否有一种方法可以记录 com.sun.jna.Structure.readField
以了解我在哪个字段中出现此错误?
JNA 抛出的“无效内存访问”错误是对 Structured Exception Handling 捕获的本机内存错误的“优雅”处理。所以要回答标题中的问题,不幸的是你无法从原生方面获得更多细节。
您真正需要知道的是,要么您正试图访问您不拥有的本机内存,要么您的本机内存分配失败.
您的映射看起来正确,但您没有显示实际调用 publish_package_fct_ptr()
方法的代码。这可能就是问题所在。
但是,它是一个回调这一事实指向了 JNA 和回调的一个非常普遍的问题,其中内存在您使用它之前被释放,所以我猜测这就是您在这里遇到的问题。
JNA 使用 Memory
class 跟踪其内存分配。如果您在构造它时没有为结构提供指针,它将分配必要的内存并将其内部存储在 Memory
object 中。这适用于结构的大多数用途。
但是,释放本机内存与 Java 的垃圾相关 collection。当 Structure 被 GC 时,其内部内存 object 也会被 GC,并且作为其 finalize()
执行的一部分,它会释放本机内存。
如果回调是您最后一次引用 Java 中的结构,它将变得不可访问并且有资格被 GC。在某些情况下,这可能会在回调完成执行之前发生,从而取消本机内存分配。
如果您使用的是 JDK 9+,在回调后使用 ReachabilityFence
将解决此问题。对于 JDK 8 及更早版本,您可能必须在回调中使用结构后对其进行一些其他代码操作,以确保 Java 保留对它的引用(及其底层本机内存)。
(Aside/unrelated 问题:如果用作方法参数,则不需要声明 Structure ByReference。这是默认设置。您可能还会发现 @FieldOrder
注释比覆盖 getFieldOrder()
方法。)