Overly eager clearing of Soft references

Description

From email to researcher's list by Tomoharu.

I have found that reference types show a strange behaviour.
When a soft reference A is reachable only though another soft
reference B, the garbage collector fails to preserve the correct
semantics.

In the attached program RefRef.java, there are 3 objects.
A "normal" object is directly reachable from a local variable, o.
A SoftReference object is also directly reachable from a local
variable, rr. Its referent, call it r, is another SoftReference object,
which in turn has as its referent, o. Immediately prior to GC,
r is soft reachable from rr (and only from rr). Thus, using the
notation s> to indicate a soft reference, and naming objects
by the variables that point to them, immediately before GC we
have:

root ---> rr s> r s> o <--- root

The specification for soft references permits two actions on a
softly reachable (and by definition, not strongly reachable) object:
it preserves the object or it may choose to reclaim it. The only softly
reachable object in this scenario is r.

In the former case, the object graph above is unchanged. Specifically,
r == rr.get() && r.get() == o.
In the latter case, after GC, rr.get() == null.
However, r.get() returns null on Jikes RVM, which should be impossible
since either r == rr.get() == null and we should get an NPE when we call r.get(),
or r == rr.get() != null and r.get() should return o.

This bug seem to be caused by an incorrect processing of
reference table (ReferenceProcessor.references). The reference
processor retains softly reachable objects (i.e. referents of soft
references) unless we have insufficient memory. This is realized
by scanning the reference table and marking referents of live soft
references. If the reference processor finds a soft reference that
is not live (not strongly reachable) in the reference table, it
disposes of this reference, clears the referent field of this
reference (a comment admits that this is paranoia) and removes the
reference from the reference table.

However, in Jikes RVM the disposed reference may still be
reachable from a referent of another, live soft reference.
Consequently, Jikes RVM incorrectly allows a user program to access
the disposed reference and to get its referent (which is now null).

The error does not lie in clearing the referent field of the disposed
(softly reachable) reference. Simply retaining the value of the referent
field would not solve the problem because, (1) the field would not be
updated if the referent were moved by a moving collector and (2) the
reference would never be enqueued.

In Jikes RVM, ReferenceProcessor is too eager to discard reference objects
that are not marked. Right at the start of
ReferenceProcessor.processReference(), we have:

/*

  • If the reference is dead, we're done with it. Let it (and

  • possibly its referent) be garbage-collected.
    */
    if (!trace.isLive(reference)) {
    clearReferent(reference); // Too much paranoia ...
    if (TRACE_UNREACHABLE) { VM.sysWriteln(" UNREACHABLE reference: ",reference); }
    if (TRACE_DETAIL) {
    VM.sysWriteln(" (unreachable)");
    }
    return ObjectReference.nullReference();
    }

This is wrong if we have chosen to preserve soft references
because this object might be reachable from a live soft reference.

A solution is as follows.

1. Usual standard transitive closure, tracing from the usual roots, marking objects.
2a. If we decide to retain softly reachable objects,
examine each SoftReference in the table of references
if the reference is marked, mark the transitive closure of its referent.
2b. If we decide to reclaim softly reachable objects,
examine each SoftReference in the table of references
if its referent is not marked, set the referent to null.
3. Reclaim any unmarked objects.

Environment

None

Assignee

Erik Brangs

Reporter

DaveG

Labels

None

Components

Fix versions

Affects versions

Priority

Medium
Configure