8314480: Memory ordering spec updates in java.lang.ref

Reviewed-by: dholmes, alanb, darcy
This commit is contained in:
Brent Christian 2024-05-31 21:29:30 +00:00
parent 9fd0e7349e
commit 2cae9a0397
4 changed files with 218 additions and 105 deletions

View file

@ -38,7 +38,7 @@ import java.util.function.Function;
* to run after the cleaner is notified that the object has become
* phantom reachable.
* The cleaner uses {@link PhantomReference} and {@link ReferenceQueue} to be
* notified when the <a href="package-summary.html#reachability">reachability</a>
* notified when the {@linkplain java.lang.ref##reachability reachability}
* changes.
* <p>
* Each cleaner operates independently, managing the pending cleaning actions
@ -215,6 +215,14 @@ public final class Cleaner {
* Refer to the <a href="#compatible-cleaners">API Note</a> above for
* cautions about the behavior of cleaning actions.
*
* <p>The given object is kept strongly reachable (and therefore not eligible
* for cleaning) during the register() method.
*
* <p>{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}:
* Actions in a thread prior to calling {@code Cleaner.register()}
* <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* the cleaning action is run by the Cleaner's thread.
*
* @param obj the object to monitor
* @param action a {@code Runnable} to invoke when the object becomes phantom reachable
* @return a {@code Cleanable} instance

View file

@ -388,11 +388,19 @@ public abstract sealed class Reference<T>
private native boolean refersTo0(Object o);
/**
* Clears this reference object. Invoking this method will not cause this
* object to be enqueued.
* Clears this reference object. Invoking this method does not enqueue this
* object, and the garbage collector will not clear or enqueue this object.
*
* <p> This method is invoked only by Java code; when the garbage collector
* clears references it does so directly, without invoking this method.
* <p>When the garbage collector or the {@link #enqueue()} method clear
* references they do so directly, without invoking this method.
*
* @apiNote
* There is a potential race condition with the garbage collector. When this
* method is called, the garbage collector may already be in the process of
* (or already completed) clearing and/or enqueueing this reference.
* Avoid this race by ensuring the referent remains strongly reachable until
* after the call to clear(), using {@link #reachabilityFence(Object)} if
* necessary.
*/
public void clear() {
clear0();
@ -470,11 +478,33 @@ public abstract sealed class Reference<T>
}
/**
* Clears this reference object and adds it to the queue with which
* it is registered, if any.
* Clears this reference object, then attempts to add it to the queue with
* which it is registered, if any.
*
* <p> This method is invoked only by Java code; when the garbage collector
* enqueues references it does so directly, without invoking this method.
* <p>If this reference is registered with a queue but not yet enqueued,
* the reference is added to the queue; this method is
* <b><i>successful</i></b> and returns true.
* If this reference is not registered with a queue, or was already enqueued
* (by the garbage collector, or a previous call to {@code enqueue}), this
* method is <b><i>unsuccessful</i></b> and returns false.
*
* <p>{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}:
* Actions in a thread prior to a <b><i>successful</i></b> call to {@code enqueue}
* <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* the reference is removed from the queue by {@link ReferenceQueue#poll}
* or {@link ReferenceQueue#remove}. <b><i>Unsuccessful</i></b> calls to
* {@code enqueue} have no specified memory consistency effects.
*
* <p> When this method clears references it does so directly, without
* invoking the {@link #clear()} method. When the garbage collector clears
* and enqueues references it does so directly, without invoking the
* {@link #clear()} method or this method.
*
* @apiNote
* Use of this method allows the registered queue's
* {@link ReferenceQueue#poll} and {@link ReferenceQueue#remove} methods
* to return this reference even though the referent may still be strongly
* reachable.
*
* @return {@code true} if this reference object was successfully
* enqueued; {@code false} if it was already enqueued or if
@ -511,60 +541,78 @@ public abstract sealed class Reference<T>
}
/**
* Ensures that the object referenced by the given reference remains
* <a href="package-summary.html#reachability"><em>strongly reachable</em></a>,
* regardless of any prior actions of the program that might otherwise cause
* the object to become unreachable; thus, the referenced object is not
* Ensures that the given object remains
* <a href="package-summary.html#reachability"><em>strongly reachable</em></a>.
* This reachability is assured regardless of any optimizing transformations
* the virtual machine may perform that might otherwise allow the object to
* become unreachable (see JLS {@jls 12.6.1}). Thus, the given object is not
* reclaimable by garbage collection at least until after the invocation of
* this method. Invocation of this method does not itself initiate garbage
* collection or finalization.
* this method. References to the given object will not be cleared (or
* enqueued, if applicable) by the garbage collector until after invocation
* of this method.
* Invocation of this method does not itself initiate reference processing,
* garbage collection, or finalization.
*
* <p> This method establishes an ordering for <em>strong reachability</em>
* with respect to garbage collection. It controls relations that are
* otherwise only implicit in a program -- the reachability conditions
* triggering garbage collection. This method is designed for use in
* uncommon situations of premature finalization where using
* {@code synchronized} blocks or methods, or using other synchronization
* facilities are not possible or do not provide the desired control. This
* method is applicable only when reclamation may have visible effects,
* which is possible for objects with finalizers (See Section {@jls 12.6}
* of <cite>The Java Language Specification</cite>) that
* are implemented in ways that rely on ordering control for
* correctness.
* triggering garbage collection. This method is applicable only
* when reclamation may have visible effects,
* such as for objects that use finalizers or {@link Cleaner}, or code that
* performs {@linkplain java.lang.ref reference processing}.
*
* <p>{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}:
* Actions in a thread prior to calling {@code reachabilityFence(x)}
* <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* the garbage collector clears any reference to {@code x}.
*
* @apiNote
* Finalization may occur whenever the virtual machine detects that no
* reference to an object will ever be stored in the heap: The garbage
* collector may reclaim an object even if the fields of that object are
* still in use, so long as the object has otherwise become unreachable.
* This may have surprising and undesirable effects in cases such as the
* following example in which the bookkeeping associated with a class is
* Reference processing or finalization can occur after an object becomes
* unreachable. An object can become unreachable when the virtual machine
* detects that there is no further need for the object (other than for
* running a finalizer). In the course of optimization, the virtual machine
* can reorder operations of an object's methods such that the object
* becomes unneeded earlier than might naively be expected &mdash;
* including while a method of the object is still running. For instance,
* the VM can move the loading of <em>values</em> from the object's fields
* to occur earlier. The object itself is then no longer needed and becomes
* unreachable, and the method can continue running using the obtained values.
* This may have surprising and undesirable effects when using a Cleaner or
* finalizer for cleanup: there is a race between the
* program thread running the method, and the cleanup thread running the
* Cleaner or finalizer. The cleanup thread could free a
* resource, followed by the program thread (still running the method)
* attempting to access the now-already-freed resource.
* Use of {@code reachabilityFence} can prevent this race by ensuring that the
* object remains strongly reachable.
* <p>
* The following is an example in which the bookkeeping associated with a class is
* managed through array indices. Here, method {@code action} uses a
* {@code reachabilityFence} to ensure that the {@code Resource} object is
* not reclaimed before bookkeeping on an associated
* {@code ExternalResource} has been performed; in particular here, to
* {@code ExternalResource} has been performed; specifically, to
* ensure that the array slot holding the {@code ExternalResource} is not
* nulled out in method {@link Object#finalize}, which may otherwise run
* concurrently.
*
* <pre> {@code
* {@snippet :
* class Resource {
* private static ExternalResource[] externalResourceArray = ...
*
* int myIndex;
* Resource(...) {
* myIndex = ...
* this.myIndex = ...
* externalResourceArray[myIndex] = ...;
* ...
* }
* protected void finalize() {
* externalResourceArray[myIndex] = null;
* externalResourceArray[this.myIndex] = null;
* ...
* }
* public void action() {
* try {
* // ...
* int i = myIndex;
* int i = this.myIndex; // last use of 'this' Resource in action()
* Resource.update(externalResourceArray[i]);
* } finally {
* Reference.reachabilityFence(this);
@ -573,36 +621,20 @@ public abstract sealed class Reference<T>
* private static void update(ExternalResource ext) {
* ext.status = ...;
* }
* }}</pre>
* }
* }
*
* Here, the invocation of {@code reachabilityFence} is nonintuitively
* The invocation of {@code reachabilityFence} is
* placed <em>after</em> the call to {@code update}, to ensure that the
* array slot is not nulled out by {@link Object#finalize} before the
* update, even if the call to {@code action} was the last use of this
* object. This might be the case if, for example a usage in a user program
* object. This might be the case if, for example, a usage in a user program
* had the form {@code new Resource().action();} which retains no other
* reference to this {@code Resource}. While probably overkill here,
* {@code reachabilityFence} is placed in a {@code finally} block to ensure
* that it is invoked across all paths in the method. In a method with more
* complex control paths, you might need further precautions to ensure that
* {@code reachabilityFence} is encountered along all of them.
*
* <p> It is sometimes possible to better encapsulate use of
* {@code reachabilityFence}. Continuing the above example, if it were
* acceptable for the call to method {@code update} to proceed even if the
* finalizer had already executed (nulling out slot), then you could
* localize use of {@code reachabilityFence}:
*
* <pre> {@code
* public void action2() {
* // ...
* Resource.update(getExternalResource());
* }
* private ExternalResource getExternalResource() {
* ExternalResource ext = externalResourceArray[myIndex];
* Reference.reachabilityFence(this);
* return ext;
* }}</pre>
* reference to this {@code Resource}.
* The {@code reachabilityFence} call is placed in a {@code finally} block to
* ensure that it is invoked across all paths in the method. A more complex
* method might need further precautions to ensure that
* {@code reachabilityFence} is encountered along all code paths.
*
* <p> Method {@code reachabilityFence} is not required in constructions
* that themselves ensure reachability. For example, because objects that
@ -612,10 +644,11 @@ public abstract sealed class Reference<T>
* blocks. (Further, such blocks must not include infinite loops, or
* themselves be unreachable, which fall into the corner case exceptions to
* the "in general" disclaimer.) However, method {@code reachabilityFence}
* remains a better option in cases where this approach is not as efficient,
* remains a better option in cases where synchronization is not as efficient,
* desirable, or possible; for example because it would encounter deadlock.
*
* @param ref the reference. If {@code null}, this method has no effect.
* @param ref the reference to the object to keep strongly reachable. If
* {@code null}, this method has no effect.
* @since 9
*/
@ForceInline

View file

@ -34,6 +34,14 @@ import jdk.internal.misc.VM;
/**
* Reference queues, to which registered reference objects are appended by the
* garbage collector after the appropriate reachability changes are detected.
*
* <p>{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}:
* The enqueueing of a reference to a queue (by the garbage collector, or by a
* successful call to {@link Reference#enqueue})
* <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* the reference is removed from the queue by {@link ReferenceQueue#poll} or
* {@link ReferenceQueue#remove}.
*
* @param <T> the type of the reference object
*
* @author Mark Reinhold
@ -175,6 +183,7 @@ public class ReferenceQueue<T> {
*
* @return A reference object, if one was immediately available,
* otherwise {@code null}
* @see java.lang.ref.Reference#enqueue()
*/
public Reference<? extends T> poll() {
if (headIsNull())
@ -206,6 +215,8 @@ public class ReferenceQueue<T> {
*
* @throws InterruptedException
* If the timeout wait is interrupted
*
* @see java.lang.ref.Reference#enqueue()
*/
public Reference<? extends T> remove(long timeout) throws InterruptedException {
if (timeout < 0)
@ -227,6 +238,7 @@ public class ReferenceQueue<T> {
*
* @return A reference object, blocking until one becomes available
* @throws InterruptedException If the wait is interrupted
* @see java.lang.ref.Reference#enqueue()
*/
public Reference<? extends T> remove() throws InterruptedException {
lock.lock();

View file

@ -32,7 +32,7 @@
* after the collector has determined that the reachability of a given
* object has changed.
*
*<h2>Package Specification</h2>
* <h2>Reference Objects</h2>
*
* A <em>reference object</em> encapsulates a reference to some other
* object so that the reference itself may be examined and manipulated
@ -44,7 +44,7 @@
* implementing canonicalizing mappings that do not prevent their keys
* (or values) from being reclaimed, and phantom references are for
* scheduling post-mortem cleanup actions.
* Post-mortem cleanup actions can be registered and managed by a
* Post-mortem cleanup actions can also be registered and managed by a
* {@link java.lang.ref.Cleaner}.
*
* <p> Each reference-object type is implemented by a subclass of the
@ -58,54 +58,21 @@
* whatever fields and methods are required for its purposes, or it
* may use these subclasses without change.
*
* <h3>Notification</h3>
*
* A program may request to be notified of changes in an object's
* reachability by <em>registering</em> an appropriate reference
* object with a <em>reference queue</em> at the time the reference
* object is created. Some time after the garbage collector
* determines that the reachability of the referent has changed to the
* value corresponding to the type of the reference, it will clear the
* reference and add it to the associated queue. At this point, the
* reference is considered to be <em>enqueued</em>. The program may remove
* references from a queue either by polling or by blocking until a
* reference becomes available. Reference queues are implemented by
* the {@link java.lang.ref.ReferenceQueue} class.
*
* <p> The relationship between a registered reference object and its
* queue is one-sided. That is, a queue does not keep track of the
* references that are registered with it. If a registered reference
* becomes unreachable itself, then it will never be enqueued. It is
* the responsibility of the program using reference objects to ensure
* that the objects remain reachable for as long as the program is
* interested in their referents.
*
* <p> While some programs will choose to dedicate a thread to
* removing reference objects from one or more queues and processing
* them, this is by no means necessary. A tactic that often works
* well is to examine a reference queue in the course of performing
* some other fairly-frequent action. For example, a hashtable that
* uses weak references to implement weak keys could poll its
* reference queue each time the table is accessed. This is how the
* {@link java.util.WeakHashMap} class works. Because
* the {@link java.lang.ref.ReferenceQueue#poll
* ReferenceQueue.poll} method simply checks an internal data
* structure, this check will add little overhead to the hashtable
* access methods.
*
* <a id="reachability"></a>
* <h3>Reachability</h3>
* <h2>Reachability</h2>
*
* Going from strongest to weakest, the different levels of
* A <em>reachable</em> object is any object that can be accessed in any potential
* continuing computation from any
* {@linkplain java.lang.Thread#isAlive live thread} (as stated in JLS {@jls 12.6.1}).
*
* <p> Going from strongest to weakest, the different levels of
* reachability reflect the life cycle of an object. They are
* operationally defined as follows:
*
* <ul>
*
* <li> An object is <em>strongly reachable</em> if it can be reached
* by some thread without traversing any reference objects. A
* newly-created object is strongly reachable by the thread that
* created it.
* <li> An object is <em>strongly reachable</em> if it is reachable and if it
* can be accessed without traversing the referent of a Reference object.
*
* <li> An object is <em>softly reachable</em> if it is not strongly
* reachable but can be reached by traversing a soft reference.
@ -125,6 +92,99 @@
*
* </ul>
*
* <h2>Notification</h2>
*
* A program may request to be notified of changes in an object's
* reachability by <em>registering</em> an appropriate reference
* object with a {@link java.lang.ref.ReferenceQueue}.
* This is done by providing the reference queue as
* a constructor argument when creating the reference object.
* Some time after the garbage collector
* determines that the reachability of the referent has changed to correspond
* with the type of the reference, it will clear the
* reference and add it to the associated queue. At this point, the
* reference is considered to be <em>enqueued</em>. The program learns of the
* referent's change in reachability when the associated reference becomes
* available on the queue. The program may remove references from a queue
* (that is, <em>dequeue</em> them) using the {@link ReferenceQueue#poll()} or
* {@link ReferenceQueue#remove()} methods. Additional state needed to respond to a
* referent's change in reachability can be stored in the fields of a custom
* reference subclass, and accessed when the reference is returned from the
* queue.
*
* <p> The relationship between a registered reference object and its
* queue is one-sided. That is, a queue does not keep track of the
* references that are registered with it. If a registered reference
* becomes unreachable itself, then it will never be enqueued. It is
* the responsibility of the program to ensure
* that reference objects remain reachable for as long as the program is
* interested in their referents.
*
* <p> While some programs will choose to dedicate a thread to
* removing reference objects from one or more queues and processing
* them, this is by no means necessary. A tactic that often works
* well is to examine a reference queue in the course of performing
* some other fairly-frequent action. For example, a hashtable that
* uses weak references to implement weak keys could poll its
* reference queue each time the table is accessed. This is how the
* {@link java.util.WeakHashMap} class works. Because
* the {@link java.lang.ref.ReferenceQueue#poll
* ReferenceQueue.poll} method simply checks an internal data
* structure, this check will add little overhead to the hashtable
* access methods.
*
* <a id="MemoryConsistency"></a>
* <h2>Memory Consistency Properties</h2>
* Certain interactions between references, reference queues, and the garbage
* collector form
* <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* relationships:
*
* <ul>
*
* <li>Actions in a thread prior to calling
* {@link Reference#reachabilityFence Reference.reachabilityFence(x)}
* <i>happen-before</i> the garbage collector clears any reference to {@code x}.</li>
*
* <li>The clearing of a reference by the garbage collector <i>happens-before</i>
* the garbage collector enqueues the reference.</li>
*
* <li>The enqueueing of a reference (by the garbage collector, or
* by a successful call to {@link Reference#enqueue}) <i>happens-before</i>
* the reference is removed from the queue (<em>dequeued</em>).</li>
*
* <li>The dequeuing of a reference to a
* {@linkplain Cleaner#register(Object object, Runnable action) registered}
* object, by the Cleaner thread, <i>happens-before</i> the Cleaner thread runs
* the cleaning action for that object.</li>
*
* </ul>
* The above chain of <i>happens-before</i> edges ensures that actions in a
* thread prior to a {@link Reference#reachabilityFence Reference.reachabilityFence(x)}
* <i>happen-before</i> cleanup code for {@code x} runs on a Cleaner thread.
* In particular, changes to the state of {@code x} made before
* {@code reachabilityFence(x)} will be visible to the cleanup code running on
* a Cleaner thread without additional synchronization.
* See JLS {@jls 17.4.5}.
*
* <p>
* The interaction between references, finalizers, and the garbage collector
* also forms a <em>happens-before</em> relationship:
*
* <ul>
* <li>Actions in a thread prior to calling
* {@link Reference#reachabilityFence Reference.reachabilityFence(x)}
* <i>happen-before</i> the finalizer for {@code x} is run by a finalizer thread.</li>
* </ul>
*
* This ensures that actions in a thread prior to a
* {@link Reference#reachabilityFence Reference.reachabilityFence(x)}
* <i>happen-before</i> cleanup code for {@code x} runs on a finalizer thread.
* In particular, changes to the state of {@code x} made before
* {@code reachabilityFence(x)} will be visible to the cleanup code running on
* a finalizer thread without additional synchronization.
* See JLS {@jls 17.4.5}.
*
* @author Mark Reinhold
* @since 1.2
*/