HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

“Removing” reference from its ReferenceQueue

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
referenceremovingitsfromreferencequeue

Problem

Sometimes I need to ensure that some references won't be processed while examination from the associated ReferenceQueue. Generally at those moments I don't know reachability status of the referent.

The only way I've came up with:

class MyReference extends SoftReference {
    private boolean active = true;

    public MyReference(V referent, ReferenceQueue queue) {
        super(referent, queue);
    }

    public void disable() {
        active = false;
    }

    public boolean isActive() {
        return active;
    }
}

// examination
Reference ref;
while ((ref = referenceQueue.poll()) != null) {
    @SuppressWarnings("unchecked")
    MyReference myRef = (MyReference) ref;
    if (myRef.isActive()) {
        // process ref
    }
}


Is this the right approach?

Solution

I believe there is a flaw in your logic that is insurmountable......

SoftReferences, once they are added to the queue, have already lost the Referent instance. I cannot think of any reason why you would want to handle an 'active' queued (the value has been GC'd) softreference in any way differently from an 'inactive' one.

Once the reference has been queued it's too late to do anything about the instance it was softly referencing.

On the other hand, if you want to 'process' a referant, and make sure it is not GC'd during the processing, then jsut keep a reference to it:

V referent = softref.get();
if (referent == null) {
    // it's already been GC'd (and at some point we will see it poll'd from the Q)
    return;
}
// do things with our referent
// since our referent varaible is a hard-reference,
//      nothing can happen to the soft reference.
....

// then clear our reference (or return from our method,
// or clear our hard reference in some other way)
referent = null;
// after this the referent can (possibly) be GC'd again


EDIT after comments:

From the Javadoc for SoftReference:


Suppose that the garbage collector determines at a certain point in time that an object is softly reachable. At that time it may choose to clear atomically all soft references to that object and all soft references to any other softly-reachable objects from which that object is reachable through a chain of strong references. At the same time or at some later time it will enqueue those newly-cleared soft references that are registered with reference queues.

The GC will only nequeue newly cleared soft references. Further, from the SoftReference.get() method, we have:


Returns this reference object's referent. If this reference object has been cleared, either by the program or by the garbage collector, then this method returns null

Finally, here is a third-party 'reference' on 'references' ... heh. It appears to be quite good.

EDIT 2: how to keep a value 'active' (responding to comments)

If the requirement is to prevent a particular Soft reference referent from being GC'd, the answer is relatively simple: create a hard reference and keep it.... For example:

private static final IdentityHashMap active = new .....;
private static final Object token = new Object();

private static final boolean forceActive(SoftReference reference) {
    MyType referent = softref.get();
    if (referent == null) {
        // it's already been GC'd ... and ...
        // if the reference was created with a ReferenceQueue, we can
        // expect to 'poll' the reference from that queue at some point
        // maybe we have already done that....
        return false;
    }
    // referent is now a hard reference, we are guaranteed that `reference`
    // is not on any Queue, and that it will not be GC'd (since we have a hard reference)

    // we want to keep our referent 'active', so we need to keep a hard reference:
    // the key in the identity hash map is our hard link.
    active.put(referent, token);
    // we exit our keep-active method, but the hard reference remains.
    return true;
}

private static final boolean deActive(SoftReference reference) {
    MyType referent = softref.get();
    if (referent == null) {
        // it's already been GC'd ... and ... that means it was not
        // in our active map (otherwise it would not have been GC'd).
        return false;
    }
    // if the map contains the referent as a key, then it will return 'token'.
    // and we have now successfully removed the hard-reference to the referent.
    return token == active.remove(referent);
}

Code Snippets

V referent = softref.get();
if (referent == null) {
    // it's already been GC'd (and at some point we will see it poll'd from the Q)
    return;
}
// do things with our referent
// since our referent varaible is a hard-reference,
//      nothing can happen to the soft reference.
....

// then clear our reference (or return from our method,
// or clear our hard reference in some other way)
referent = null;
// after this the referent can (possibly) be GC'd again
private static final IdentityHashMap<MyType, Object> active = new .....;
private static final Object token = new Object();

private static final boolean forceActive(SoftReference<MyType> reference) {
    MyType referent = softref.get();
    if (referent == null) {
        // it's already been GC'd ... and ...
        // if the reference was created with a ReferenceQueue, we can
        // expect to 'poll' the reference from that queue at some point
        // maybe we have already done that....
        return false;
    }
    // referent is now a hard reference, we are guaranteed that `reference`
    // is not on any Queue, and that it will not be GC'd (since we have a hard reference)

    // we want to keep our referent 'active', so we need to keep a hard reference:
    // the key in the identity hash map is our hard link.
    active.put(referent, token);
    // we exit our keep-active method, but the hard reference remains.
    return true;
}

private static final boolean deActive(SoftReference<MyType> reference) {
    MyType referent = softref.get();
    if (referent == null) {
        // it's already been GC'd ... and ... that means it was not
        // in our active map (otherwise it would not have been GC'd).
        return false;
    }
    // if the map contains the referent as a key, then it will return 'token'.
    // and we have now successfully removed the hard-reference to the referent.
    return token == active.remove(referent);
}

Context

StackExchange Code Review Q#28534, answer score: 3

Revisions (0)

No revisions yet.