OpenJDK / bsd-port / jdk9 / jdk
changeset 13484:827ce3d74163
8133348: Reference.reachabilityFence
Reviewed-by: plevart, mr, chegar, mchung
Contributed-by: dl@cs.oswego.edu, aleksey.shipilev@oracle.com, paul.sandoz@oracle.com
author | psandoz |
---|---|
date | Tue, 06 Oct 2015 18:42:06 +0200 |
parents | 8e32a37dd7f5 |
children | 4591cb19f8ea |
files | src/java.base/share/classes/java/lang/ref/Reference.java test/java/lang/ref/ReachabilityFenceTest.java |
diffstat | 2 files changed, 260 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/ref/Reference.java Wed Dec 09 15:26:52 2015 +0100 +++ b/src/java.base/share/classes/java/lang/ref/Reference.java Tue Oct 06 18:42:06 2015 +0200 @@ -25,6 +25,7 @@ package java.lang.ref; +import jdk.internal.vm.annotation.DontInline; import sun.misc.Cleaner; import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangRefAccess; @@ -311,4 +312,120 @@ this.queue = (queue == null) ? ReferenceQueue.NULL : queue; } + /** + * 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 + * 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. + * + * <p> This method establishes an ordering for + * <a href="package-summary.html#reachability"><em>strong reachability</em></a> + * 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 + * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6"> + * Section 12.6 17 of <cite>The Java™ Language Specification</cite></a>) + * that are implemented in ways that rely on ordering control for correctness. + * + * @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 + * 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 + * 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 + * class Resource { + * private static ExternalResource[] externalResourceArray = ... + * + * int myIndex; + * Resource(...) { + * myIndex = ... + * externalResourceArray[myIndex] = ...; + * ... + * } + * protected void finalize() { + * externalResourceArray[myIndex] = null; + * ... + * } + * public void action() { + * try { + * // ... + * int i = myIndex; + * Resource.update(externalResourceArray[i]); + * } finally { + * Reference.reachabilityFence(this); + * } + * } + * private static void update(ExternalResource ext) { + * ext.status = ...; + * } + * }}</pre> + * + * Here, the invocation of {@code reachabilityFence} is nonintuitively + * 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 + * 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> + * + * <p> Method {@code reachabilityFence} is not required in constructions + * that themselves ensure reachability. For example, because objects that + * are locked cannot, in general, be reclaimed, it would suffice if all + * accesses of the object, in all methods of class {@code Resource} + * (including {@code finalize}) were enclosed in {@code synchronized (this)} + * 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, + * desirable, or possible; for example because it would encounter deadlock. + * + * @param ref the reference. If {@code null}, this method has no effect. + * @since 9 + */ + @DontInline + public static void reachabilityFence(Object ref) { + // Does nothing, because this method is annotated with @DontInline + // HotSpot needs to retain the ref and not GC it before a call to this + // method + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/ref/ReachabilityFenceTest.java Tue Oct 06 18:42:06 2015 +0200 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8133348 + * @summary Tests if reachabilityFence is working + * + * @run main/othervm -Xint -Dpremature=false ReachabilityFenceTest + * @run main/othervm -XX:TieredStopAtLevel=1 -Dpremature=true ReachabilityFenceTest + * @run main/othervm -XX:TieredStopAtLevel=2 -Dpremature=true ReachabilityFenceTest + * @run main/othervm -XX:TieredStopAtLevel=3 -Dpremature=true ReachabilityFenceTest + * @run main/othervm -XX:TieredStopAtLevel=4 -Dpremature=true ReachabilityFenceTest + */ + +import java.lang.ref.Reference; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ReachabilityFenceTest { + + /* + * Implementation notes: + * + * This test has positive and negative parts. + * + * Negative test is "nonFenced", and it tests that absent of reachabilityFence, the object can + * be prematurely finalized -- this validates the test itself. Not every VM mode is expected to + * prematurely finalize the objects, and -Dpremature option communicates that to test. If a VM mode + * passes the negative test, then our understanding of what could happen is correct, and we can + * go forward. + * + * Positive test is "fenced", and it checks that given the reachabilityFence at the end of the block, + * the object cannot be finalized. There is no sense running a positive test when premature finalization + * is not expected. It is a job for negative test to verify that invariant. + * + * The test methods should be appropriately compiled, therefore we do several iterations. + */ + + // Enough to OSR and compile + static final int LOOP_ITERS = Integer.getInteger("LOOP_ITERS", 50000); + + // Enough after which to start triggering GC and finalization + static final int WARMUP_LOOP_ITERS = LOOP_ITERS - Integer.getInteger("GC_ITERS", 100); + + // Enough to switch from an OSR'ed method to compiled method + static final int MAIN_ITERS = 3; + + static final boolean PREMATURE_FINALIZATION = Boolean.getBoolean("premature"); + + public static void main(String... args) { + // Negative test + boolean finalized = false; + for (int c = 0; !finalized && c < MAIN_ITERS; c++) { + finalized |= nonFenced(); + } + + if (PREMATURE_FINALIZATION && !finalized) { + throw new IllegalStateException("The object had never been finalized before timeout reached."); + } + + if (!PREMATURE_FINALIZATION && finalized) { + throw new IllegalStateException("The object had been finalized without a fence, even though we don't expect it."); + } + + if (!PREMATURE_FINALIZATION) + return; + + // Positive test + finalized = false; + for (int c = 0; !finalized && c < MAIN_ITERS; c++) { + finalized |= fenced(); + } + + if (finalized) { + throw new IllegalStateException("The object had been prematurely finalized."); + } + } + + public static boolean nonFenced() { + AtomicBoolean finalized = new AtomicBoolean(); + MyFinalizeable o = new MyFinalizeable(finalized); + + for (int i = 0; i < LOOP_ITERS; i++) { + if (finalized.get()) break; + if (i > WARMUP_LOOP_ITERS) { + System.gc(); + System.runFinalization(); + } + } + + return finalized.get(); + } + + public static boolean fenced() { + AtomicBoolean finalized = new AtomicBoolean(); + MyFinalizeable o = new MyFinalizeable(finalized); + + for (int i = 0; i < LOOP_ITERS; i++) { + if (finalized.get()) break; + if (i > WARMUP_LOOP_ITERS) { + System.gc(); + System.runFinalization(); + } + } + + Reference.reachabilityFence(o); + + return finalized.get(); + } + + private static class MyFinalizeable { + private final AtomicBoolean finalized; + + public MyFinalizeable(AtomicBoolean b) { + this.finalized = b; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + finalized.set(true); + } + } +} \ No newline at end of file