10

What's the state of an object when you manually enqueue a reference?

this.s = "foo";
WeakReference<String> wr = new WeakReference<String>(this.s);
wr.enqueue();

All the documentation I've found talks about the garbage collector enqueueing the object, not about what happens when you do it manually.

And is there any situation where this would make sense? What does it mean for an object to be enqueued, but to still have reachable references (strong, weak, phantom)?

Edit: extra bonus followup question: does the object get enqueued again when it becomes unreachable some time in the distant future?

James Moore
  • 8,636
  • 5
  • 71
  • 90
  • What possible benefit do you think you're getting from manually enqueueing the reference? The reference queue exists to notify you that something has happened, not to trigger behavior. – kdgregory Apr 12 '11 at 13:21
  • @kdgregory - that's exactly the question I'm asking. Why would you do it, and if you do, what does it mean? There's a way to do it manually, so presumably it's not completely useless. – James Moore Apr 12 '11 at 17:06
  • the JDK is large enough, and old enough, that there are lots of bits that have no good explanation. Perhaps someone thought it was a good idea at the time. Perhaps it was needed by some early revision of the JDK and then couldn't be deleted for backwards compatibility. While I applaud your desire to learn, I think that learning for the sake of learning is rather pointless. – kdgregory Apr 12 '11 at 17:50
  • 7
    @kdgregory, I think you're wrong here. Learning about how things work is almost always useful (and learning for its own sake is a good thing, period). And the JDK just isn't that old. Lots of people who worked on it in 1996 are still active professionally, and there's vast amounts of information on its development process. – James Moore Apr 13 '11 at 16:17

4 Answers4

2

There is a good article about types of references in Java: http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html

In few words: You can do something like this:

public class Main {
    public static void main(String[] args) throws InterruptedException{
        String weakString = "Help me";
        ReferenceQueue<String> refQ = new ReferenceQueue<String>();
        WeakReference<String> weakRef = new WeakReference<String>(weakString, refQ);
        //there is no more "hard reference to the string"
        weakString = null;
        //there is no object in ReferenceQueue
        System.out.println(refQ.poll());
        //put the object in the ReferenceQueue
        weakRef.enqueue();
        while (true) {
            //There will be returned "Help me" until the garbage collector will "kill" 
            //"weak" string. This will be done after some time because there are no                                     
            //more strong references to the string. After that will be returned a null  
            //object.
            System.out.println(weakRef.get());
            //only one time there will be returned a refenrece to the String object
            System.out.println(refQ.poll());
            Thread.sleep(1000);
        }
    }
}

This is designed for working with some kind of cache. In this way you can be sure that there won't be non-used object in the memory for long time. The ReferenceQueue could be used for performing some kind of cleanup after calling weakRef.enqueue();. For example closing files or streams that was used to work with data.

StKiller
  • 7,631
  • 10
  • 43
  • 56
  • I saw that article, but it seems to get something wrong: "The only use for such a reference is keeping track of when it gets enqueued into a ReferenceQueue, as at that point you know the object to which it pointed is dead." If you can enqueue something manually, while it still has reachable references, then you can't assume it's dead just because it's been enqueued. – James Moore Apr 07 '11 at 19:45
  • Imho, ReferenceQueue gives the posibility to manualy clear some data that is related to the collected object. So if you have a method that periodicaly checks the queue and clears the object, the "enqueue" method offers you a possibility to clear a object before it gets collected by GC. – StKiller Apr 07 '11 at 19:53
  • But you can just clear it - there's no need to do the enqueue. – James Moore Apr 08 '11 at 05:37
  • I mean - there is another thread that responses for clearing weak objects. In another thread/part of you application you can enqueue the useless object and in this way to give a signal to the "cleaner" thread to do it's work. – StKiller Apr 08 '11 at 05:40
  • @StKiller Your example does not work. Literal String's never get GC'd.. Allocate the Strring as follows: String weakString = new String("Help me!"); – Matej 'Yin' Gagyi May 08 '16 at 15:13
  • @StKiller Your example still does not work as described in the comments. Once the WeakReference was enqueued, GC never reclaims is. See: https://github.com/yin/java-crafts/blob/master/com/github/yin/java/crafts/memory/WeakReferenceGCExample.java – Matej 'Yin' Gagyi May 08 '16 at 16:04
2

The strong reference would remain, so the object is not eligible for collection. As JVestry points out, if you create the weak reference with a queue, then wr.enqueue() returns true as expected.

A potential use for this would be to have your handler be able to operate on an object subject to either its pending garbage collection or some other system state change where you would want to take the same action as if the object were being collected, e.g. maybe you keep a soft reference to something that holds system resources so that the system can handle a low memory situation while you are still able to manage closing the resources yourself if you finish normally.

On the second question, a reference is only enqueued once, whether you do it with enqueue() or the gc does it for as a part of a collection.

EDIT It is important to remember that the relationship between the referencequeue is to the reference, in this case wr, and not the referent, in this case s. You could have multiple weak references to the same object and achieve multiple enqueues (sort of), one for each weak reference. But, it is the weak reference that is in the queue, not the referent, although it ought to still be reachable from the weak reference. See javadoc on java.lang.ref in the Notification section for more details.

philwb
  • 3,805
  • 19
  • 20
  • I don't mean to be overly anal about this one, but you never know how many other references there is to the weakly referenced object in other parts of the code, especially if it is a resource. If you can discard such an object, you should have a method to flag it so and perform resource release then. Why? Because forcing other references to check an enqueued weak reference is a blattant violation of OO design. The status of the object does not lie in the object itself anymore. It is also an open door to crazy runtime bugs... – Jérôme Verstrynge Apr 14 '11 at 15:46
  • @JVerstry - If I am managing access to a resource with a soft reference then yes, I do need to check the result of reference.get() as the referent may have been collected. This is not a violation of any design principles. If the resource is being shared then, regardless the use of a soft reference or of enqueue to close a resource, no access to the resource can safely assume a state condition and all access must have some basic check and error handling. The use of enqueue to close the resource is just reuse of the mechanism that I must have in place to handle the collection of the referent. – philwb Apr 14 '11 at 17:38
  • "it is the weak reference that is in the queue, not the referent" was something that should have been obvious to me but wasn't, for some reason. – James Moore Apr 16 '11 at 00:28
1

In your case, wr.enqueue() will return false, because you have not specified a queue when creating the WeakReference. If you provide a ReferenceQueue when creating the WeakReference, wr.enqueue() will return true.

Enqueuing the weak reference manually does not make sense. It is like writing down that the weak reference has been garbage collected when it has not: 's' is still a strong reference to "foo".

Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
  • What's the enqueue() method used for? If it can never make sense, why's it there? (The doc is very explicit about the garbage collector itself not using enqueue(), so that's not it.) – James Moore Apr 07 '11 at 19:49
  • This is most probably needed for compilation purposes and byte code generation. You need a grip in byte code to call corresponding enqueue() bytecode, but Java won't call the method, because it can be overridden. If it did, user code could cause quite a mess and disrupt garbage collection... – Jérôme Verstrynge Apr 07 '11 at 20:26
-1

is there any situation where this would make sense?

ReferenceQueues are sometimes used for resource management, in such a case calling enqueue() yourself to release the resources before the GC runs would make sense, as some resources are rather limited (this could be done in a dispose() method with the weak reference itself as a safety).

added: This can be useful if cleanup takes time, since referencequeues can be read in their own thread it wont hang your main thread.

What does it mean for an object to be enqueued, but to still have reachable references (strong, weak, phantom)?

The documentations states that it is invoked after the object reference is set to null, so enqueue() can not influence the object state directly The current documentation states that the enqueue call is not used by the gc, it is meant to queue a Reference before the gc does it. The referenced object will not be affected by this call, the cleanup code reading the referencequeue could however dispose resources used by that object.

Edits: Checked the 1.6 docs now, so corrected the error about gc using enqueue (the older docs didn't point this out, it does however make sense to limit the gc's exposure to user code)

josefx
  • 15,506
  • 6
  • 38
  • 63
  • "calling enqueue() yourself to release the resources before the GC runs would make sense" -> Not at all, the resource would not necessarily be released just by calling enqueue(). You would only be fooling yourself. Moreover, other weak reference to the same object might not be enqueued themselves. – Jérôme Verstrynge Apr 07 '11 at 18:55
  • @JVerstry it would naturally depend on the cleanupcode that handles the queue. I meant it can be usefull WHEN the reference has a referencequeue does resourcehandling and in that case enquequeing one reference would be enough for the cleanup code reading the queue. – josefx Apr 07 '11 at 19:33
  • In this case, you are much better off cleaning up 'now' rather than delegating the cleaning up to something monitoring the queue. – Jérôme Verstrynge Apr 07 '11 at 19:44
  • @JVerstry a reference queue may be read on its own thread, doing the "cleanup now" when there are thread sensitive operations involved with the cleanup a call to enqueue would be the easiest way to dispose resources manually. – josefx Apr 07 '11 at 23:17