setting and returning a Java object from native code

108 views
Skip to first unread message

Kyle Hayes

unread,
Jan 26, 2022, 1:42:11 PM1/26/22
to Java Native Access
Perhaps I am just not asking the right question but I cannot figure out how to safely pass a Java object reference to native code and have it return that object as an argument to a callback back into Java code some indeterminate time later.   It could be seconds or hours later.   Or longer.  More than long enough for a full GC cycle (or more) to happen.

While I can see how to pass a Java object reference (it looks like it gets passed as a long, i.e. 64-bit on 64-bit platforms), what I am not sure about is what happens when I store that on the native side for long periods of time.   Does JNA make sure that the GC does not move the object?   If not, is there a way to pin it or otherwise make sure that the GC does not move it around?

The call sequence would be:

Java -> native: register a Java callback object or Lamdba with an additional object argument to pass to the callback when it is called.

time passes...

native -> Java: call the callback with several arguments, one of which is the Java object passed in the first step.

I have callbacks working well and seamlessly but I am only passing either integers or C strings to the Java callback method/Lambda.  No Java objects.

Any tips would be appreciated!

Best,
Kyle

Matthias Bläsing

unread,
Jan 26, 2022, 3:35:21 PM1/26/22
to jna-...@googlegroups.com
Hi,

Am Mittwoch, dem 26.01.2022 um 10:42 -0800 schrieb Kyle Hayes:
> [Store object ref in native code for callback]
>
> While I can see how to pass a Java object reference (it looks like it
> gets passed as a long, i.e. 64-bit on 64-bit platforms), what I am
> not sure about is what happens when I store that on the native side
> for long periods of time.   Does JNA make sure that the GC does not
> move the object?   If not, is there a way to pin it or otherwise make
> sure that the GC does not move it around?

No it does not. In fact, with out specifying
Library#OPTION_ALLOW_OPTIONS, no java object will reach native code.
Even if you activate that you are not done yet. If you want to use the
references beyond the method call, you'd need to add a global reference
using JNI calls on the native side. I suggest a different approach:

You already need a mechanism to keep your callbacks alive on Java side,
so why not add a field to the callback object and hold the data there.
An alternative (if callback and data object are not related) is a map
and passing the key to native code (beware: if you use string keys, you
need to copy on the native side).

Maybe this helps?!

Greetings

Matthias

Kyle Hayes

unread,
Feb 15, 2022, 7:40:05 PM2/15/22
to Java Native Access
Oops, I see I never responded to this!

See inline.

I looked around for a while on how to do this.   I looked at making a wrapper with JNI calls to add a global reference but that broke my goal of trying to use the C DLL directly.

It is easiest to do it exactly as you said.  I just capture the object I need when creating the callback object.  I ended up adding a new C API function (I own the C library and the Java wrapper) since a user sent in a PR to do it.   This C function takes a C function pointer for the callback and a void pointer for the data.   I don't use it in the Java wrapper.

It turns out that C# does have the ability to pin an object in memory so that the GC does not move it.  For that wrapper, the new API function is very helpful.

Thanks for the tips!

Best,
Kyle

Reply all
Reply to author
Forward
0 new messages