Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JNIEnv::take_rust_field seems meant for use with java.lang.Object.finalize, but that is deprecated #535

Open
argv-minus-one opened this issue May 1, 2024 · 0 comments

Comments

@argv-minus-one
Copy link
Contributor

When using JNIEnv::set_rust_field, there does not appear to be any non-deprecated way to guarantee that the native memory will eventually be deallocated.

In the old days, the standard way to free native memory resources was to override the java.lang.Object.finalize() method. In this case, finalize could be a native method that calls JNIEnv::take_rust_field.

This still works, more or less, but finalize has issues and has been deprecated since Java 9. The current best practice is to use a java.lang.ref.Cleaner. When using that, though, there is no way to read the fields of the original object, and therefore no way to use JNIEnv::take_rust_field. By the time the cleanup function is called, the original object is already gone.

So, to use Cleaner to free Rust memory, one would need to do something like this:

class Example {
	private static final Cleaner CLEANER = Cleaner.create();

	private final long ptr;

	private Example(long ptr) {
		this.ptr = ptr;
		CLEANER.register(this, () -> free(ptr));
	}

	private static native void free(long ptr);
}

That is, the pointer is stored in two places:

  1. In the instance field Example.ptr. Instance methods of Example read this field.
  2. As a captured variable in the function passed to Cleaner.register. When the corresponding instance of Example is garbage collected, this cleanup function calls the static native method free with the pointer.

Besides not using any deprecated Java methods, this also has the advantage that it does not involve any mutex locking. Neither the Java monitor lock nor a Rust mutex is needed to safely read the native memory that Example.ptr points to, as long as there is a live reference to the Example instance that owns it (although writing would still need some sort of synchronization device, just like with Rust Arc).

But although the Java code is pretty straightforward, the Rust side of this is complicated and delicate. I wonder if there's some way to wrap a nice API around it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant