diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix index 7504ed52c324d..1781d84ab942f 100644 --- a/make/hotspot/symbols/symbols-unix +++ b/make/hotspot/symbols/symbols-unix @@ -171,11 +171,13 @@ JVM_NativePath JVM_NewArray JVM_NewInstanceFromConstructor JVM_NewMultiArray +JVM_PhantomReferenceRefersTo JVM_RaiseSignal JVM_RawMonitorCreate JVM_RawMonitorDestroy JVM_RawMonitorEnter JVM_RawMonitorExit +JVM_ReferenceRefersTo JVM_RegisterLambdaProxyClassForArchiving JVM_RegisterSignal JVM_ReleaseUTF diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index a6fc3ad2ff142..2ce053487ae7f 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -904,8 +904,9 @@ class java_lang_ref_Reference: AllStatic { public: // Accessors - static inline oop referent(oop ref); - static inline void set_referent(oop ref, oop value); + static inline oop weak_referent_no_keepalive(oop ref); + static inline oop phantom_referent_no_keepalive(oop ref); + static inline oop unknown_referent_no_keepalive(oop ref); static inline void set_referent_raw(oop ref, oop value); static inline HeapWord* referent_addr_raw(oop ref); static inline oop next(oop ref); diff --git a/src/hotspot/share/classfile/javaClasses.inline.hpp b/src/hotspot/share/classfile/javaClasses.inline.hpp index 3e880da8ae5ed..44d62dab5daca 100644 --- a/src/hotspot/share/classfile/javaClasses.inline.hpp +++ b/src/hotspot/share/classfile/javaClasses.inline.hpp @@ -100,12 +100,17 @@ bool java_lang_String::is_instance_inlined(oop obj) { } // Accessors -oop java_lang_ref_Reference::referent(oop ref) { - return ref->obj_field(_referent_offset); + +oop java_lang_ref_Reference::weak_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); +} + +oop java_lang_ref_Reference::phantom_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); } -void java_lang_ref_Reference::set_referent(oop ref, oop value) { - ref->obj_field_put(_referent_offset, value); +oop java_lang_ref_Reference::unknown_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); } void java_lang_ref_Reference::set_referent_raw(oop ref, oop value) { diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index 820839dae61a1..d634ffa9efd76 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -262,7 +262,7 @@ void DiscoveredListIterator::load_ptrs(DEBUG_ONLY(bool allow_null_referent)) { _next_discovered = discovered; _referent_addr = java_lang_ref_Reference::referent_addr_raw(_current_discovered); - _referent = java_lang_ref_Reference::referent(_current_discovered); + _referent = java_lang_ref_Reference::unknown_referent_no_keepalive(_current_discovered); assert(Universe::heap()->is_in_or_null(_referent), "Wrong oop found in java.lang.Reference object"); assert(allow_null_referent ? @@ -1058,7 +1058,7 @@ ReferenceProcessor::add_to_discovered_list_mt(DiscoveredList& refs_list, // cleared concurrently by mutators during (or after) discovery. void ReferenceProcessor::verify_referent(oop obj) { bool da = discovery_is_atomic(); - oop referent = java_lang_ref_Reference::referent(obj); + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); assert(da ? oopDesc::is_oop(referent) : oopDesc::is_oop_or_null(referent), "Bad referent " INTPTR_FORMAT " found in Reference " INTPTR_FORMAT " during %satomic discovery ", @@ -1119,7 +1119,8 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { // known to be strongly reachable. if (is_alive_non_header() != NULL) { verify_referent(obj); - if (is_alive_non_header()->do_object_b(java_lang_ref_Reference::referent(obj))) { + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); + if (is_alive_non_header()->do_object_b(referent)) { return false; // referent is reachable } } @@ -1169,7 +1170,7 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { // .. we are an atomic collector and referent is in our span if (is_subject_to_discovery(obj) || (discovery_is_atomic() && - is_subject_to_discovery(java_lang_ref_Reference::referent(obj)))) { + is_subject_to_discovery(java_lang_ref_Reference::unknown_referent_no_keepalive(obj)))) { } else { return false; } diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 6e09e076d09c2..35220e2ea6052 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -330,6 +330,15 @@ JVM_HasReferencePendingList(JNIEnv *env); JNIEXPORT void JNICALL JVM_WaitForReferencePendingList(JNIEnv *env); +JNIEXPORT jboolean JNICALL +JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o); + +/* + * java.lang.ref.PhantomReference + */ +JNIEXPORT jboolean JNICALL +JVM_PhantomReferenceRefersTo(JNIEnv *env, jobject ref, jobject o); + /* * java.io.ObjectInputStream */ diff --git a/src/hotspot/share/oops/instanceRefKlass.cpp b/src/hotspot/share/oops/instanceRefKlass.cpp index b8de138d59e55..4b30a8edfe6a0 100644 --- a/src/hotspot/share/oops/instanceRefKlass.cpp +++ b/src/hotspot/share/oops/instanceRefKlass.cpp @@ -79,7 +79,7 @@ void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) { void InstanceRefKlass::oop_verify_on(oop obj, outputStream* st) { InstanceKlass::oop_verify_on(obj, st); // Verify referent field - oop referent = java_lang_ref_Reference::referent(obj); + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); if (referent != NULL) { guarantee(oopDesc::is_oop(referent), "referent field heap failed"); } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 2ac00310f3741..4705b6a935cc9 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3439,6 +3439,24 @@ JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env)) } JVM_END +JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o)) + JVMWrapper("JVM_ReferenceRefersTo"); + oop ref_oop = JNIHandles::resolve_non_null(ref); + oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop); + return referent == JNIHandles::resolve(o); +JVM_END + + +// java.lang.ref.PhantomReference ////////////////////////////////////////////////// + + +JVM_ENTRY(jboolean, JVM_PhantomReferenceRefersTo(JNIEnv* env, jobject ref, jobject o)) + JVMWrapper("JVM_PhantomReferenceRefersTo"); + oop ref_oop = JNIHandles::resolve_non_null(ref); + oop referent = java_lang_ref_Reference::phantom_referent_no_keepalive(ref_oop); + return referent == JNIHandles::resolve(o); +JVM_END + // ObjectInputStream /////////////////////////////////////////////////////////////// diff --git a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java index b79e3385a7f3c..d653dc7ffba31 100644 --- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java +++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, 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 @@ -60,6 +60,14 @@ public T get() { return null; } + /* Override the implementation of Reference.refersTo. + * Phantom references are weaker than finalization, so the referent + * access needs to be handled differently for garbage collectors that + * do reference processing concurrently. + */ + @Override + native final boolean refersTo0(Object o); + /** * Creates a new phantom reference that refers to the given object and * is registered with the given queue. 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 132680c175361..e655a85792b25 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -325,14 +325,40 @@ public void runFinalization() { * been cleared, either by the program or by the garbage collector, then * this method returns {@code null}. * + * @apiNote + * This method returns a strong reference to the referent. This may cause + * the garbage collector to treat it as strongly reachable until some later + * collection cycle. The {@link #refersTo(Object) refersTo} method can be + * used to avoid such strengthening when testing whether some object is + * the referent of a reference object; that is, use {@code ref.refersTo(obj)} + * rather than {@code ref.get() == obj}. + * * @return The object to which this reference refers, or * {@code null} if this reference object has been cleared + * @see refersTo */ @IntrinsicCandidate public T get() { return this.referent; } + /** + * Tests if the referent of this reference object is {@code obj}. + * Using a {@code null} {@code obj} returns {@code true} if the + * reference object has been cleared. + * + * @param obj the object to compare with this reference object's referent + * @return {@code true} if {@code obj} is the referent of this reference object + * @since 16 + */ + public final boolean refersTo(T obj) { + return refersTo0(obj); + } + + /* Implementation of refersTo(), overridden for phantom references. + */ + native boolean refersTo0(Object o); + /** * Clears this reference object. Invoking this method will not cause this * object to be enqueued. diff --git a/src/java.base/share/native/libjava/PhantomReference.c b/src/java.base/share/native/libjava/PhantomReference.c new file mode 100644 index 0000000000000..b6d6e7297f104 --- /dev/null +++ b/src/java.base/share/native/libjava/PhantomReference.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include "jvm.h" +#include "java_lang_ref_PhantomReference.h" + +JNIEXPORT jboolean JNICALL +Java_java_lang_ref_PhantomReference_refersTo0(JNIEnv *env, jobject ref, jobject o) +{ + return JVM_PhantomReferenceRefersTo(env, ref, o); +} diff --git a/src/java.base/share/native/libjava/Reference.c b/src/java.base/share/native/libjava/Reference.c index 92c518c5d90ba..20d33e6ead640 100644 --- a/src/java.base/share/native/libjava/Reference.c +++ b/src/java.base/share/native/libjava/Reference.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, 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 @@ -43,3 +43,9 @@ Java_java_lang_ref_Reference_waitForReferencePendingList(JNIEnv *env, jclass ign { JVM_WaitForReferencePendingList(env); } + +JNIEXPORT jboolean JNICALL +Java_java_lang_ref_Reference_refersTo0(JNIEnv *env, jobject ref, jobject o) +{ + return JVM_ReferenceRefersTo(env, ref, o); +} diff --git a/test/hotspot/jtreg/gc/TestReferenceRefersTo.java b/test/hotspot/jtreg/gc/TestReferenceRefersTo.java new file mode 100644 index 0000000000000..09d4fec3d14a2 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestReferenceRefersTo.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2020, 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. + */ + +package gc; + +/* @test + * @requires vm.gc != "Epsilon" + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @modules java.base + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * gc.TestReferenceRefersTo + */ + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import sun.hotspot.WhiteBox; + +public class TestReferenceRefersTo { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static final class TestObject { + public final int value; + + public TestObject(int value) { + this.value = value; + } + } + + private static volatile TestObject testObjectNone = null; + private static volatile TestObject testObject1 = null; + private static volatile TestObject testObject2 = null; + private static volatile TestObject testObject3 = null; + private static volatile TestObject testObject4 = null; + + private static ReferenceQueue queue = null; + + private static PhantomReference testPhantom1 = null; + + private static WeakReference testWeak2 = null; + private static WeakReference testWeak3 = null; + private static WeakReference testWeak4 = null; + + private static void setup() { + testObjectNone = new TestObject(0); + testObject1 = new TestObject(1); + testObject2 = new TestObject(2); + testObject3 = new TestObject(3); + testObject4 = new TestObject(4); + + queue = new ReferenceQueue(); + + testPhantom1 = new PhantomReference(testObject1, queue); + + testWeak2 = new WeakReference(testObject2, queue); + testWeak3 = new WeakReference(testObject3, queue); + testWeak4 = new WeakReference(testObject4, queue); + } + + private static void gcUntilOld(Object o) throws Exception { + if (!WB.isObjectInOldGen(o)) { + WB.fullGC(); + if (!WB.isObjectInOldGen(o)) { + fail("object not promoted by full gc"); + } + } + } + + private static void gcUntilOld() throws Exception { + gcUntilOld(testObjectNone); + gcUntilOld(testObject1); + gcUntilOld(testObject2); + gcUntilOld(testObject3); + gcUntilOld(testObject4); + + gcUntilOld(testPhantom1); + + gcUntilOld(testWeak2); + gcUntilOld(testWeak3); + gcUntilOld(testWeak4); + } + + private static void progress(String msg) { + System.out.println(msg); + } + + private static void fail(String msg) throws Exception { + throw new RuntimeException(msg); + } + + private static void expectCleared(Reference ref, + String which) throws Exception { + expectNotValue(ref, testObjectNone, which); + if (!ref.refersTo(null)) { + fail("expected " + which + " to be cleared"); + } + } + + private static void expectNotCleared(Reference ref, + String which) throws Exception { + expectNotValue(ref, testObjectNone, which); + if (ref.refersTo(null)) { + fail("expected " + which + " to not be cleared"); + } + } + + private static void expectValue(Reference ref, + TestObject value, + String which) throws Exception { + expectNotValue(ref, testObjectNone, which); + expectNotCleared(ref, which); + if (!ref.refersTo(value)) { + fail(which + " doesn't refer to expected value"); + } + } + + private static void expectNotValue(Reference ref, + TestObject value, + String which) throws Exception { + if (ref.refersTo(value)) { + fail(which + " refers to unexpected value"); + } + } + + private static void checkInitialStates() throws Exception { + expectValue(testPhantom1, testObject1, "testPhantom1"); + expectValue(testWeak2, testObject2, "testWeak2"); + expectValue(testWeak3, testObject3, "testWeak3"); + expectValue(testWeak4, testObject4, "testWeak4"); + } + + private static void discardStrongReferences() { + // testObjectNone not dropped + testObject1 = null; + testObject2 = null; + // testObject3 not dropped + testObject4 = null; + } + + private static void testConcurrentCollection() throws Exception { + progress("setup concurrent collection test"); + setup(); + progress("gcUntilOld"); + gcUntilOld(); + + progress("acquire control of concurrent cycles"); + WB.concurrentGCAcquireControl(); + try { + progress("check initial states"); + checkInitialStates(); + + progress("discard strong references"); + discardStrongReferences(); + + progress("run GC to before marking completed"); + WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED); + + progress("fetch test objects, possibly keeping some alive"); + expectNotCleared(testPhantom1, "testPhantom1"); + expectNotCleared(testWeak2, "testWeak2"); + expectValue(testWeak3, testObject3, "testWeak3"); + + // For some collectors, calling get() will keep testObject4 alive. + if (testWeak4.get() == null) { + fail("testWeak4 unexpectedly == null"); + } + + progress("finish collection"); + WB.concurrentGCRunToIdle(); + + progress("verify expected clears"); + expectCleared(testPhantom1, "testPhantom1"); + expectCleared(testWeak2, "testWeak2"); + expectValue(testWeak3, testObject3, "testWeak3"); + // This is true for all currently supported concurrent collectors. + expectNotCleared(testWeak4, "testWeak4"); + + progress("verify get returns expected values"); + if (testWeak2.get() != null) { + fail("testWeak2.get() != null"); + } + + TestObject obj3 = testWeak3.get(); + if (obj3 == null) { + fail("testWeak3.get() returned null"); + } else if (obj3.value != 3) { + fail("testWeak3.get().value is " + obj3.value); + } + + TestObject obj4 = testWeak4.get(); + if (obj4 == null) { + fail("testWeak4.get() returned null"); + } else if (obj4.value != 4) { + fail("testWeak4.get().value is " + obj4.value); + } + + progress("verify queue entries"); + long timeout = 60000; // 1 minute of milliseconds. + while (true) { + Reference ref = queue.remove(timeout); + if (ref == null) { + break; + } else if (ref == testPhantom1) { + testPhantom1 = null; + } else if (ref == testWeak2) { + testWeak2 = null; + } else if (ref == testWeak3) { + testWeak3 = null; + } else if (ref == testWeak4) { + testWeak4 = null; + } else { + fail("unexpected reference in queue"); + } + } + if (testPhantom1 != null) { + fail("testPhantom1 not notified"); + } else if (testWeak2 != null) { + fail("testWeak2 not notified"); + } else if (testWeak3 == null) { + fail("testWeak3 notified"); + } else if (testWeak4 == null) { + if (obj4 != null) { + fail("testWeak4 notified"); + } + } + + } finally { + progress("release control of concurrent cycles"); + WB.concurrentGCReleaseControl(); + } + progress("finished concurrent collection test"); + } + + private static void testSimpleCollection() throws Exception { + progress("setup simple collection test"); + setup(); + progress("gcUntilOld"); + gcUntilOld(); + + progress("check initial states"); + checkInitialStates(); + + progress("discard strong references"); + TestObject tw4 = testWeak4.get(); // Keep testObject4 alive. + discardStrongReferences(); + + progress("collect garbage"); + WB.fullGC(); + + progress("verify expected clears"); + expectCleared(testPhantom1, "testPhantom1"); + expectCleared(testWeak2, "testWeak2"); + expectValue(testWeak3, testObject3, "testWeak3"); + expectNotCleared(testWeak4, "testWeak4"); + + progress("verify get returns expected values"); + if (testWeak2.get() != null) { + fail("testWeak2.get() != null"); + } else if (testWeak3.get() != testObject3) { + fail("testWeak3.get() is not expected value"); + } else if (testWeak4.get() != tw4) { + fail("testWeak4.get() is not expected value"); + } + + progress("finished simple collection test"); + } + + public static void main(String[] args) throws Exception { + if (WB.supportsConcurrentGCBreakpoints()) { + testConcurrentCollection(); + } + testSimpleCollection(); + } +} diff --git a/test/jdk/java/lang/ref/ReferenceRefersTo.java b/test/jdk/java/lang/ref/ReferenceRefersTo.java new file mode 100644 index 0000000000000..8ccaf4f3d534f --- /dev/null +++ b/test/jdk/java/lang/ref/ReferenceRefersTo.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020, 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 8188055 + * @summary Basic functional test of Reference.refersTo. + */ + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.PhantomReference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; + +public class ReferenceRefersTo { + private static final void fail(String msg) throws Exception { + throw new RuntimeException(msg); + } + + private static final void test(T ref, + Object expectedValue, + Object unexpectedValue, + String kind) throws Exception { + if ((expectedValue != null) && ref.refersTo(null)) { + fail(kind + "refers to null"); + } + if (!ref.refersTo(expectedValue)) { + fail(kind + " doesn't refer to expected value"); + } + if (ref.refersTo(unexpectedValue)) { + fail(kind + " refers to unexpected value"); + } + } + + public static void main(String[] args) throws Exception { + var queue = new ReferenceQueue(); + + var obj0 = new Object(); + var obj1 = new Object(); + var obj2 = new Object(); + var obj3 = new Object(); + + var pref = new PhantomReference(obj0, queue); + var wref = new WeakReference(obj1); + var sref = new SoftReference(obj2); + + test(pref, obj0, obj3, "phantom"); + test(wref, obj1, obj3, "weak"); + test(sref, obj2, obj3, "soft"); + + pref.clear(); + wref.clear(); + sref.clear(); + + test(pref, null, obj3, "phantom"); + test(wref, null, obj3, "weak"); + test(sref, null, obj3, "soft"); + } +}