Is it possible to make GC manage native object'

2019-04-29 22:05发布

With C++ and C# experience and some little Java knowledge I'm now starting a Java+JNI (C++) project (Android, if that matters).

I have a native method, that creates some C++ class and returns a pointer to it as a Java long value (say, handle). And then other native methods called from Java code here and there, use the handle as a parameter to do some native operations on this class. C++ side does not own the object, it's Java side who does. But in the current architecture design it's hard to define who exactly owns the object and when to delete it. So it would probably be nice to make Java VM garbage collector to manage the object's lifetime somehow. The C++ class does not consume any resources, except some piece of memory, not large. So it's OK, if several such objects will not be destructed.

In C# I would probably wrap the native IntPtr handle in some managed wrapper class. And override it's finalizer to call native object's destructor when the managed wrapper is garbage collected. SafeHandle, AddMemoryPressure, etc. might be also of help here.

This is a different story with Java's finalize. The second thing you know after 'Hello world' in Java, is that using finalize is bad. Are there any other ways to accomplish this in Java? Maybe using PhantomReference?

4条回答
劳资没心,怎么记你
2楼-- · 2019-04-29 22:28

If you understand why you should avoid using Java's finalize method, you will also understand how to use it correctly. Using finalize for closing system resources (files and handles) is bad because you don't actually know when those resources will be closed and released. Using complex finalize logic is bad as your object reference can leak out and get pinned in memory again.

For your scenario, it is perfectly fine to use finalize.

查看更多
乱世女痞
3楼-- · 2019-04-29 22:30

Well let's consider the reason WHY finalize and Co are problematic: As you know there's no guarantee that the finalize will be called before the VM is shut down, which means that special cleanup code won't necessarily run (imo a bad decision, I don't see any problems to run through the finalize queue at cleanup, but well that's how it is). Also this is exactly the same situation in C#

Now your objects only consume memory, which will be cleaned up by the OS anyhow when the VM is destroyed, so the only case where finalize is problematic won't matter for you. So yes you can indeed use this variant and it'll work perfectly fine, but it may not exactly be considered a great architectural design - and as soon as you add resources to your C++ code where the OS doesn't handle the cleanup correctly you will run into problems

Also note that implementing a finalizer results in some additional overhead for the GC and means it takes two cycles to cleanup one of these objects (and whatever you do, don't ever save an object in the finalize method)

查看更多
Fickle 薄情
4楼-- · 2019-04-29 22:42

So how can we achieve it using phantom reference.

  1. Create a wrapper object for your native intPtr object. Create a phantom reference(with a reference queue) on the wrapper object.
  2. Create and maintain a map of phantom reference to intPtr.
  3. Create a thread that will be monitoring the reference queue for finalized wrapper object instances.
  4. This thread will get the phantom reference from reference queue, lookup intPtr using phantom reference and call destructor on native int object referenced by intPtr.
  5. While all this happening, you can go about happily using the wrapper object in your java code.
查看更多
贼婆χ
5楼-- · 2019-04-29 22:43

using a wrapper with a finalizer is a decent solution here

but if you really don't wanna do that you can use a PhantomReference with a ReferenceQueue to clean it up (but you are going to require a separate thread to poll the queue)

查看更多
登录 后发表回答