I have the following error when invoking Java code from C using JNA:
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)
In my use case the C code invoke the publish_package_fct_pt
function which is mapped to a Java interface method. This method has only one structure argument:
void publish_package_fct_ptr(void (*pf) (PackageStruct));
I use the following library:
public interface MyLib extends Library {
public void publish_package_fct_ptr(InvokerPackage callback);
public interface InvokerPackage extends Callback {
void invoke(PackageStruct.ByReference pack);
}
}
with the following structure definition:
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();
}
}
The C structure is:
struct PackageStruct {
long id;
int type;
int subtype;
const char *configuration;
const char *name;
long date;
};
It worked correctly before I added the configuration
field, and I have the exception now. I "think" that the configuration
field points to nothing, which might be the reason why I have this error.
Do you agree with my hypothesis? And is there a mean in JNA to log the com.sun.jna.Structure.readField
to know for which field I have this error?
The "Invalid Memory Access" error thrown by JNA is a "graceful" handling of native memory errors caught by Structured Exception Handling. So to answer the question in the title, unfortunately you can't get more details from the native side.
All you really need to know is that either You are attempting to access native memory that you do not own or Your native memory allocation failed.
Your mappings look correct, but you haven't shown the code where you actually call the publish_package_fct_ptr()
method. That's where the problem probably exists.
However, the fact that it's a callback points to a very common problem with JNA and callbacks, in which the memory is released before you use it so I'll hazard a guess that's the problem you are experiencing here.
JNA tracks its memory allocation using the Memory
class. If you don't provide the Structure a pointer when you construct it, it will allocate the necessary memory and store that internally in a Memory
object. This works for most uses of structures.
However, releasing the native memory is tied to Java's garbage collection. When the Structure is GC'd its internal memory object is also GC'd and as part of its finalize()
execution it releases the native memory.
If the callback is the last time you reference the Structure in Java, it becomes unreachable and is eligible to be GC'd. In some cases, this could happen before the callback finishes executing, taking away that native memory allocation.
If you are using JDK 9+, using a ReachabilityFence
after the callback will solve this problem. For JDK 8 and earlier, you may have to do some other code manipulation of the Structure after it's used in the callback, to ensure Java retains a reference to it (and its underlying native memory).
(Aside/unrelated to the question: you don't need to declare a Structure ByReference if used as a method argument. That's the default. You might also find the @FieldOrder
annotation cleaner than the override of the getFieldOrder()
method.)