From 2cae9a0397f4e46c6faec0a998ecad1c7015564d Mon Sep 17 00:00:00 2001 From: Brent Christian Date: Fri, 31 May 2024 21:29:30 +0000 Subject: [PATCH] 8314480: Memory ordering spec updates in java.lang.ref Reviewed-by: dholmes, alanb, darcy --- .../share/classes/java/lang/ref/Cleaner.java | 10 +- .../classes/java/lang/ref/Reference.java | 155 +++++++++++------- .../classes/java/lang/ref/ReferenceQueue.java | 12 ++ .../classes/java/lang/ref/package-info.java | 146 ++++++++++++----- 4 files changed, 218 insertions(+), 105 deletions(-) diff --git a/src/java.base/share/classes/java/lang/ref/Cleaner.java b/src/java.base/share/classes/java/lang/ref/Cleaner.java index 004051a9b89..b9126ad4163 100644 --- a/src/java.base/share/classes/java/lang/ref/Cleaner.java +++ b/src/java.base/share/classes/java/lang/ref/Cleaner.java @@ -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 reachability + * notified when the {@linkplain java.lang.ref##reachability reachability} * changes. *

* Each cleaner operates independently, managing the pending cleaning actions @@ -215,6 +215,14 @@ public final class Cleaner { * Refer to the API Note above for * cautions about the behavior of cleaning actions. * + *

The given object is kept strongly reachable (and therefore not eligible + * for cleaning) during the register() method. + * + *

{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}: + * Actions in a thread prior to calling {@code Cleaner.register()} + * happen-before + * 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 diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index ef4fd1ef20f..f29a293ce02 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -388,11 +388,19 @@ public abstract sealed class Reference 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. * - *

This method is invoked only by Java code; when the garbage collector - * clears references it does so directly, without invoking this method. + *

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 } /** - * 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. * - *

This method is invoked only by Java code; when the garbage collector - * enqueues references it does so directly, without invoking this method. + *

If this reference is registered with a queue but not yet enqueued, + * the reference is added to the queue; this method is + * successful 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 unsuccessful and returns false. + * + *

{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}: + * Actions in a thread prior to a successful call to {@code enqueue} + * happen-before + * the reference is removed from the queue by {@link ReferenceQueue#poll} + * or {@link ReferenceQueue#remove}. Unsuccessful calls to + * {@code enqueue} have no specified memory consistency effects. + * + *

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 } /** - * Ensures that the object referenced by the given reference remains - * strongly reachable, - * 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 + * strongly reachable. + * 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. * *

This method establishes an ordering for strong reachability * 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 The Java Language Specification) 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}. + * + *

{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}: + * Actions in a thread prior to calling {@code reachabilityFence(x)} + * happen-before + * 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 — + * including while a method of the object is still running. For instance, + * the VM can move the loading of values 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. + *

+ * 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. * - *

 {@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
      *   private static void update(ExternalResource ext) {
      *     ext.status = ...;
      *   }
-     * }}
+ * } + * } * - * Here, the invocation of {@code reachabilityFence} is nonintuitively + * The invocation of {@code reachabilityFence} is * placed after 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. - * - *

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}: - * - *

 {@code
-     * public void action2() {
-     *   // ...
-     *   Resource.update(getExternalResource());
-     * }
-     * private ExternalResource getExternalResource() {
-     *   ExternalResource ext = externalResourceArray[myIndex];
-     *   Reference.reachabilityFence(this);
-     *   return ext;
-     * }}
+ * 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. * *

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 * 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 diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index 83fb6b4ca14..9cbfb733706 100644 --- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -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. + * + *

{@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}) + * happens-before + * the reference is removed from the queue by {@link ReferenceQueue#poll} or + * {@link ReferenceQueue#remove}. + * * @param the type of the reference object * * @author Mark Reinhold @@ -175,6 +183,7 @@ public class ReferenceQueue { * * @return A reference object, if one was immediately available, * otherwise {@code null} + * @see java.lang.ref.Reference#enqueue() */ public Reference poll() { if (headIsNull()) @@ -206,6 +215,8 @@ public class ReferenceQueue { * * @throws InterruptedException * If the timeout wait is interrupted + * + * @see java.lang.ref.Reference#enqueue() */ public Reference remove(long timeout) throws InterruptedException { if (timeout < 0) @@ -227,6 +238,7 @@ public class ReferenceQueue { * * @return A reference object, blocking until one becomes available * @throws InterruptedException If the wait is interrupted + * @see java.lang.ref.Reference#enqueue() */ public Reference remove() throws InterruptedException { lock.lock(); diff --git a/src/java.base/share/classes/java/lang/ref/package-info.java b/src/java.base/share/classes/java/lang/ref/package-info.java index 3f37878650c..099fb148f77 100644 --- a/src/java.base/share/classes/java/lang/ref/package-info.java +++ b/src/java.base/share/classes/java/lang/ref/package-info.java @@ -32,7 +32,7 @@ * after the collector has determined that the reachability of a given * object has changed. * - *

Package Specification

+ *

Reference Objects

* * A reference object 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}. * *

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. * - *

Notification

- * - * A program may request to be notified of changes in an object's - * reachability by registering an appropriate reference - * object with a reference queue 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 enqueued. 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. - * - *

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. - * - *

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. - * * - *

Reachability

+ *

Reachability

* - * Going from strongest to weakest, the different levels of + * A reachable 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}). + * + *

Going from strongest to weakest, the different levels of * reachability reflect the life cycle of an object. They are * operationally defined as follows: * *

* + *

Notification

+ * + * A program may request to be notified of changes in an object's + * reachability by registering 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 enqueued. 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, dequeue 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. + * + *

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. + * + *

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. + * + * + *

Memory Consistency Properties

+ * Certain interactions between references, reference queues, and the garbage + * collector form + * happens-before + * relationships: + * + * + * The above chain of happens-before edges ensures that actions in a + * thread prior to a {@link Reference#reachabilityFence Reference.reachabilityFence(x)} + * happen-before 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}. + * + *

+ * The interaction between references, finalizers, and the garbage collector + * also forms a happens-before relationship: + * + *

+ * + * This ensures that actions in a thread prior to a + * {@link Reference#reachabilityFence Reference.reachabilityFence(x)} + * happen-before 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 */