LIfecycle of mapping of native resources in Java (

2019-08-17 13:03发布

问题:

Please consider this scenario:

There is a connection between Java and some (expensive) native resources, which is a requirement to be mapped to Java. The question is about the lifecycle of the resources and how these will be available on both native and java, as long as they are needed. Let's assume that we can have an active JNI connection both ways, as required.

The rules are as follows:

  • The resources will stay live as long as the wrapper Java object needs them. The Java object could be garbage collected if desired.

  • The resources will stay live as long as the native part needs them.

  • There should exist a mechanism to "reincarnate" the Java object if it has been GC'ed. This Java wrapper object might have fields, not mapped to the native resources, which need to appear in the reincarnated object.

  • There is a mechanism to inform the native part that Java, as a client, doesn't need the resource any more (with no guarantee that it will not request it again in the future) as well as to inform Java that native has decided that no other clients want the resources any more.

Possible list of solutions (and why they won't work):

  • Keep a Map of WeakReference of wrappers: there will be a problem with the possible java fields which will be GC'ed and unable to recreate

  • Using "finalize" and recycle data: it is not possible since it is called at most once by GC.

  • Using .clone() : It could be overriden and possibly loose data

  • Use Reflection to gather all private fields and reapply on the newly create Java object: the constructor is unknown (and may produce various side effects)

  • ... ?

Here is the original (wrong) question. Any ideas how to secure the connection between these resources and Java?

回答1:

Managing resources properly requires maintaining a sense of ownership (possibly transferable), where the owner of each resource is responsible for releasing that resource when it becomes unneeded. You seem to be describing a system where ownership of the native resources is obscured -- this is the problem you need to solve.

It sounds like you need to some kind of resource manager, which may itself need both Java and native components. This:

  • There is a mechanism to inform the native part that Java, as a client, doesn't need the resource any more (with no guarantee that it will not request it again in the future) as well as to inform Java that native has decided that no other clients want the resources any more.

makes me think that you may already have part of one, but your design seems to go out of the way to make things hard. In particular, this:

  • There should exist a mechanism to "reincarnate" the Java object if it has been GC'ed. This Java wrapper object might have fields, not mapped to the native resources, which need to appear in the reincarnated object.

more or less means that no, in fact you can't allow those Java-side objects to be GC'd. You need to maintain their state, or at least an essential part of it, on the Java side. The resource manager needs to track that, and prevent it from being GC'd as long as your policy demands that.

Here's one approach to designing something like that:

  • On the Java side, the resource manager maintains an association between native resources and Java-side properties. Possibly there is some kind of essential data object that for that, between the wrappers used by clients and the underlying native resource.

  • As requested, the resource manager hands out instances of the main Java-side wrapper objects by which clients access the native resources and associated Java data.

  • The resource manager has a mechanism to find out when those main Java-side objects are no longer needed. That could be via a ReferenceQueue -- do not rely on finalizers -- but I recommend instead making them Closeable, and making their users responsible for closing them when they are done with them.

  • The Java and the native sides of the resource manager cooperate to determine when to release the native resources, based on whatever policy you choose. Simplest would be to release them when there are no longer any clients on either side, but you'll need to decide whether that's consistent with the ability to "reincarnate" wrapper objects.

It does sound like you need a richer set of messages between the Java and native sides than you've described so far, or at least additional semantics for the messages you already have. For example, you may need messages that say "there is at least one client on this side again". Also you may need messages to negotiate for resource removal, or at least return values for your existing messages to indicate whether resource removal should take place.

Also, do take care to make all this thread-safe. Even if you stipulate that clients are responsible for using individual wrappers in a thread-safe manner, you would not merely be asking for, but practically demanding trouble if the resource manager components were not thread-safe.