Search code examples
javamultithreadinggarbage-collectionobject-lifetime

How can I safely and timely dispose a scarce shared resouce in Java?


In a parallel application, threads(32) in a thread group use a shared unmanaged and standalone disposable object.

We have the same thing in our c/c++ app, and there I use shared_ptr<> in order to let object dispose and finalize just after there is no need to the object.

I just tried to apply the same thing in Java, and I faced with finalize() method. But there are some problems, because of GC is so lazy sometimes, the object doesn't even identified as unreachable object for disposing/finalizing, sometimes it is called, but there is no warranty GC lets the object invoke the finalize() completely.

So I just came with another complex solution which I just count down and track the threads are using the object which it doesn't work too, but I know this is not a reliable solution, and I know I will faced with unexpected results.

I'm just wonder if there is something equivalent to shared_ptr<> in java, or is it possible to handle the object via JNI?

Any ideas?


Solution

  • Doing what you want well needs some effort, and will never feel natural in Java, because deterministic cleanup of resources is foreign to Java. It has gotten a bit better since Java 7 though.

    The best way to work around it is this:

    1. Add a counter of type java.util.concurrent.AtomicInteger to your java wrapper, initialised at 1 (Thanks @Jules for that, now avoiding synchronized!).
    2. Add an addRef method, which throws if the counter is 0, returning this for better use in the try-statement.
    3. Implement java.lang.AutoCloseable: close decreases the count when not 0, and releases the resource when the count reaches 0.
    4. Add a finalizer as a final safety-net: Log failure to properly release the resource earlier, and do the final release.
    5. Add a comment to every variable / argument which owns such a reference and is not a try-with-resource, so you know to call addRef and close as appropriate.

    A try-with-resources-block using the java wrapper:

    try(resource.AddRef()) {
        // Do your thing
    }