0

In Java, is there any way to tell whether or not references are being maintained to an object in other threads (or generally)?

Consider the following class:

public class ResourcePool<TYPE>
{
    private final Queue<Object> emptyLocks = new LinkedBlockingQueue<>();

    private final Queue<TYPE> available = new LinkedBlockingQueue<>();
    private final Queue<TYPE> claimed = new LinkedBlockingQueue<>();

    public void add(TYPE resource)
    {
        available.add(resource);
    }

    public TYPE claim() throws InterruptedException
    {
        if (available.isEmpty())
        {
            Object lock = new Object();
            synchronized (lock)
            {
                emptyLocks.add(lock);
                lock.wait();
            }
        }

        TYPE resource = available.poll();
        claimed.add(resource);
        return resource;
    }

    public void release(TYPE resource)
    {
        if (!claimed.remove(resource)) return;
        available.add(resource);

        Object lock = emptyLocks.poll();
        if (lock != null) synchronized (lock) {lock.notify();}
    }
}

The idea here is that multiple threads can claim/release resources such that two threads can never own the same resource at any given moment. But what happens if a thread forgets to release() a resource? Worse yet, what if the thread calls release() and then keeps doing stuff with the resource?

Using the WeakReference class, it's possible to tell when there exist no more strong references to a given object. However, when that happens, the object is garbage-collected and it's gone. SoftReference might work, but then there's still a chance that our resource will be GC'd before we can put it back in the "available" list.

So here's the question: Is there any way to keep track of whether these resources are still actually being used?

Ideally, threads could claim() resources, use them as long as they want, and those resources would be freed up automatically as soon as no more references are maintained. I think that would be very elegant, and useful in other situations too.

SmashMaster
  • 111
  • 1
  • 9

2 Answers2

1

The answer is simple: No

GC works at VM global level and in current implementation (at least in Hotspot) it doesn't use reference counting, much less reference tracking. That means even the VM doesn't always know what is referenced at arbitrary points in time.

The GC is also generally the wrong tool for tracking things that need to be released in a timely manner; the GC may run only very infrequently (in extreme cases the GC may be tuned to run once every few hours). You probably want your resources to be available to other threads as soon as the owning block scope ends; a requirement vastly different from the lax handling by the GC.

What you want is immediate detection for things going out of scope per thread. While a nice feature, the question is if that would ever be worth the performance impact it costs.

The example of your resources and a thread "forgetting" to release / retaining the resource after release is a something that can be dealt with by convention: use try-finally or maybe abuse try-with-resources blocks to make sure the life cycle is properly maintained.

To catch accidental leaking of a resource, you can abuse finalize; but again its not predicatble when resources get finalized (and you can only do cleanup, resurrecting the resource should not be done; see the JLS about finalize for that).

Edit: You can easily defend against rouge threads using a resource after release by stroing the owner in the resource. The resource can then do runtime checking that the calling thread is indeed the current owner.

Durandal
  • 19,919
  • 4
  • 36
  • 70
0

You're searching for the finalize() method. You have to override this method to clean up when an object is not reachable any more.

Please note that creating a finalize() method is not as easy as it looks like (as explained in these answers).

Community
  • 1
  • 1
Uwe Plonus
  • 9,803
  • 4
  • 41
  • 48
  • But then, once you've detected that an object is no longer used, there isn't anything you can do to resurrect it reliably. (You can kind of resurrect it by assigning a reference somewhere else, but objects are never finalized twice, so that just means you won't detect the next leak) – user253751 Apr 29 '15 at 10:01
  • @immibis I understood the question the way that the poster wanted to release a resource when the using object is garbage collected. This is what the `finalize()` method is for. I know that programming a proper `finalize()` method is not so easy (therefore this link to the other question). – Uwe Plonus Apr 29 '15 at 10:04
  • @UwePlonus I think you need more warnings here. Otherwise the author might think that `finalize()` is a handy method to clean up resources (based on his "I think that would be very elegant, and useful in other situations too." comment). – Kayaman Apr 29 '15 at 10:12
  • Use of finalize() is discouraged. You don't know when it's going to be called, it even couldn't be called at all. Unless you know exactly what you are doing, do not override finalize(). – Hakan Apr 29 '15 at 10:18
  • I think a nice way to describe the limits of finalize is to say: The GC runs when the heap is running low on free memory. It does _not_ necessarily run when the system is running out of file handles, or when its running out of available TCP ports, or when it's running out of... – Solomon Slow Apr 29 '15 at 13:24