diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 8151acd99e0..64a2e6456d8 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -141,7 +141,7 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_arraycopy: case vmIntrinsics::_currentTimeMillis: case vmIntrinsics::_nanoTime: - case vmIntrinsics::_Reference_get: + case vmIntrinsics::_Reference_get0: // Use the intrinsic version of Reference.get() so that the value in // the referent field can be registered by the G1 pre-barrier code. // Also to prevent commoning reads from this field across safepoint diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index c11017fc442..1e4e07360cc 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -3340,7 +3340,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope) break; } - case vmIntrinsics::_Reference_get: + case vmIntrinsics::_Reference_get0: { { // With java.lang.ref.reference.get() we must go through the diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 341de0ac0c2..850a196c898 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1185,7 +1185,7 @@ void LIRGenerator::do_Return(Return* x) { // Example: ref.get() // Combination of LoadField and g1 pre-write barrier -void LIRGenerator::do_Reference_get(Intrinsic* x) { +void LIRGenerator::do_Reference_get0(Intrinsic* x) { const int referent_offset = java_lang_ref_Reference::referent_offset(); @@ -2914,8 +2914,8 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) { case vmIntrinsics::_onSpinWait: __ on_spin_wait(); break; - case vmIntrinsics::_Reference_get: - do_Reference_get(x); + case vmIntrinsics::_Reference_get0: + do_Reference_get0(x); break; case vmIntrinsics::_updateCRC32: diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index e70bbd96189..ec0ea5dc047 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -266,7 +266,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void do_CompareAndSwap(Intrinsic* x, ValueType* type); void do_PreconditionsCheckIndex(Intrinsic* x, BasicType type); void do_FPIntrinsics(Intrinsic* x); - void do_Reference_get(Intrinsic* x); + void do_Reference_get0(Intrinsic* x); void do_update_CRC32(Intrinsic* x); void do_update_CRC32C(Intrinsic* x); void do_vectorizedMismatch(Intrinsic* x); diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index baa945cdddf..dd28e1a898c 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -99,7 +99,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) { case vmIntrinsics::_dpow: case vmIntrinsics::_Preconditions_checkIndex: case vmIntrinsics::_Preconditions_checkLongIndex: - case vmIntrinsics::_Reference_get: + case vmIntrinsics::_Reference_get0: case vmIntrinsics::_Continuation_doYield: case vmIntrinsics::_updateCRC32: case vmIntrinsics::_updateBytesCRC32: @@ -244,7 +244,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_storeFence: case vmIntrinsics::_fullFence: case vmIntrinsics::_countPositives: - case vmIntrinsics::_Reference_get: + case vmIntrinsics::_Reference_get0: case vmIntrinsics::_Continuation_doYield: case vmIntrinsics::_Continuation_enterSpecial: case vmIntrinsics::_Continuation_pin: diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index eeefddfedfc..5be372075ed 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -461,7 +461,7 @@ class methodHandle; do_signature(vectorizedMismatch_signature, "(Ljava/lang/Object;JLjava/lang/Object;JII)I") \ \ /* java/lang/ref/Reference */ \ - do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ + do_intrinsic(_Reference_get0, java_lang_ref_Reference, get0_name, void_object_signature, F_RN) \ do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \ do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \ do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index dc9ce61627b..c562e3e6ccf 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -422,7 +422,7 @@ class SerializeClosure; template(sp_name, "sp") \ template(pc_name, "pc") \ template(cs_name, "cs") \ - template(get_name, "get") \ + template(get0_name, "get0") \ template(refersTo0_name, "refersTo0") \ template(clear0_name, "clear0") \ template(put_name, "put") \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 73f60765a70..f97374553ca 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -353,6 +353,9 @@ JVM_HasReferencePendingList(JNIEnv *env); JNIEXPORT void JNICALL JVM_WaitForReferencePendingList(JNIEnv *env); +JNIEXPORT jobject JNICALL +JVM_ReferenceGet(JNIEnv *env, jobject ref); + JNIEXPORT jboolean JNICALL JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o); diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 1de7dd824f8..ad39169bca0 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -148,7 +148,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan case vmIntrinsics::_fmaF: return java_lang_math_fmaF; case vmIntrinsics::_dsqrt: return java_lang_math_sqrt; case vmIntrinsics::_dsqrt_strict: return java_lang_math_sqrt_strict; - case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get; + case vmIntrinsics::_Reference_get0: return java_lang_ref_reference_get0; case vmIntrinsics::_Object_init: if (m->code_size() == 1) { // We need to execute the special return bytecode to check for @@ -210,7 +210,7 @@ vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) { case java_lang_math_exp : return vmIntrinsics::_dexp; case java_lang_math_fmaD : return vmIntrinsics::_fmaD; case java_lang_math_fmaF : return vmIntrinsics::_fmaF; - case java_lang_ref_reference_get: return vmIntrinsics::_Reference_get; + case java_lang_ref_reference_get0: return vmIntrinsics::_Reference_get0; case java_util_zip_CRC32_update : return vmIntrinsics::_updateCRC32; case java_util_zip_CRC32_updateBytes : return vmIntrinsics::_updateBytesCRC32; @@ -320,7 +320,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) { case java_util_zip_CRC32_updateByteBuffer : tty->print("java_util_zip_CRC32_updateByteBuffer"); break; case java_util_zip_CRC32C_updateBytes : tty->print("java_util_zip_CRC32C_updateBytes"); break; case java_util_zip_CRC32C_updateDirectByteBuffer: tty->print("java_util_zip_CRC32C_updateDirectByteByffer"); break; - case java_lang_ref_reference_get : tty->print("java_lang_ref_reference_get"); break; + case java_lang_ref_reference_get0 : tty->print("java_lang_ref_reference_get0"); break; case java_lang_Thread_currentThread : tty->print("java_lang_Thread_currentThread"); break; case java_lang_Float_float16ToFloat : tty->print("java_lang_Float_float16ToFloat"); break; case java_lang_Float_floatToFloat16 : tty->print("java_lang_Float_floatToFloat16"); break; diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index b6876b3a2da..a3e93aa0a30 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -83,7 +83,7 @@ class AbstractInterpreter: AllStatic { java_lang_math_exp, // implementation of java.lang.Math.exp (x) java_lang_math_fmaF, // implementation of java.lang.Math.fma (x, y, z) java_lang_math_fmaD, // implementation of java.lang.Math.fma (x, y, z) - java_lang_ref_reference_get, // implementation of java.lang.ref.Reference.get() + java_lang_ref_reference_get0, // implementation of java.lang.ref.Reference.get() java_util_zip_CRC32_update, // implementation of java.util.zip.CRC32.update() java_util_zip_CRC32_updateBytes, // implementation of java.util.zip.CRC32.updateBytes() java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer() diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 533c88cce9e..928d1ac9f9c 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -204,7 +204,7 @@ void TemplateInterpreterGenerator::generate_all() { method_entry(java_lang_math_pow ) method_entry(java_lang_math_fmaF ) method_entry(java_lang_math_fmaD ) - method_entry(java_lang_ref_reference_get) + method_entry(java_lang_ref_reference_get0) AbstractInterpreter::initialize_method_handle_entries(); method_entry(java_util_zip_CRC32C_updateBytes) @@ -228,6 +228,7 @@ void TemplateInterpreterGenerator::generate_all() { // entries for `native` methods to use the same address in case // intrinsic is disabled. native_method_entry(java_lang_Thread_currentThread) + native_method_entry(java_lang_ref_reference_get0) native_method_entry(java_util_zip_CRC32_update) native_method_entry(java_util_zip_CRC32_updateBytes) @@ -465,7 +466,7 @@ address TemplateInterpreterGenerator::generate_intrinsic_entry(AbstractInterpret case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break; case Interpreter::java_lang_math_sqrt_strict : entry_point = generate_math_entry(Interpreter::java_lang_math_sqrt); break; - case Interpreter::java_lang_ref_reference_get + case Interpreter::java_lang_ref_reference_get0 : entry_point = generate_Reference_get_entry(); break; case Interpreter::java_util_zip_CRC32_update : entry_point = generate_CRC32_update_entry(); break; diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp index c4eeb3fa840..8fa0835216d 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp @@ -64,7 +64,7 @@ void ZeroInterpreterGenerator::generate_all() { method_entry(java_lang_math_exp ); method_entry(java_lang_math_fmaD ); method_entry(java_lang_math_fmaF ); - method_entry(java_lang_ref_reference_get); + method_entry(java_lang_ref_reference_get0); AbstractInterpreter::initialize_method_handle_entries(); @@ -107,7 +107,7 @@ address ZeroInterpreterGenerator::generate_method_entry( case Interpreter::java_lang_math_exp : // fall thru case Interpreter::java_lang_math_fmaD : // fall thru case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break; - case Interpreter::java_lang_ref_reference_get + case Interpreter::java_lang_ref_reference_get0 : entry_point = generate_Reference_get_entry(); break; default: fatal("unexpected method kind: %d", kind); diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index ea8b6853298..e5b1e131505 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -767,7 +767,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_doubleToRawLongBits: case vmIntrinsics::_doubleToLongBits: case vmIntrinsics::_longBitsToDouble: - case vmIntrinsics::_Reference_get: + case vmIntrinsics::_Reference_get0: case vmIntrinsics::_Reference_refersTo0: case vmIntrinsics::_PhantomReference_refersTo0: case vmIntrinsics::_Reference_clear0: diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index ebc2b28e4ad..4c5f382ceee 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -783,19 +783,9 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, StartNode* s = new StartNode(root(), tf()->domain()); initial_gvn()->set_type_bottom(s); verify_start(s); - if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) { - // With java.lang.ref.reference.get() we must go through the - // intrinsic - even when get() is the root - // method of the compile - so that, if necessary, the value in - // the referent field of the reference object gets recorded by - // the pre-barrier code. - cg = find_intrinsic(method(), false); - } - if (cg == nullptr) { - float past_uses = method()->interpreter_invocation_count(); - float expected_uses = past_uses; - cg = CallGenerator::for_inline(method(), expected_uses); - } + float past_uses = method()->interpreter_invocation_count(); + float expected_uses = past_uses; + cg = CallGenerator::for_inline(method(), expected_uses); } if (failing()) return; if (cg == nullptr) { diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index e454be2b848..94f047dc115 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -564,7 +564,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass(); - case vmIntrinsics::_Reference_get: return inline_reference_get(); + case vmIntrinsics::_Reference_get0: return inline_reference_get0(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); @@ -6923,9 +6923,9 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() { return true; } -//----------------------------inline_reference_get---------------------------- +//----------------------------inline_reference_get0---------------------------- // public T java.lang.ref.Reference.get(); -bool LibraryCallKit::inline_reference_get() { +bool LibraryCallKit::inline_reference_get0() { const int referent_offset = java_lang_ref_Reference::referent_offset(); // Get the argument: diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 136d0979615..a622eefa248 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -300,7 +300,7 @@ class LibraryCallKit : public GraphKit { bool inline_bitshuffle_methods(vmIntrinsics::ID id); bool inline_compare_unsigned(vmIntrinsics::ID id); bool inline_divmod_methods(vmIntrinsics::ID id); - bool inline_reference_get(); + bool inline_reference_get0(); bool inline_reference_refersTo0(bool is_phantom); bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index d669d7bf5ec..1389bf6d602 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3040,9 +3040,17 @@ JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env)) } JVM_END +JVM_ENTRY(jobject, JVM_ReferenceGet(JNIEnv* env, jobject ref)) + oop ref_oop = JNIHandles::resolve_non_null(ref); + // PhantomReference has its own implementation of get(). + assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition"); + oop referent = java_lang_ref_Reference::weak_referent(ref_oop); + return JNIHandles::make_local(THREAD, referent); +JVM_END + JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o)) oop ref_oop = JNIHandles::resolve_non_null(ref); - // PhantomReference has it's own implementation of refersTo(). + // PhantomReference has its own implementation of refersTo(). // See: JVM_PhantomReferenceRefersTo assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition"); oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop); 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 c83f197380a..ef2e5e0d0c4 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -357,11 +357,18 @@ public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> * {@code null} if this reference object has been cleared * @see #refersTo */ - @IntrinsicCandidate public T get() { - return this.referent; + return get0(); } + /* Implementation of get(). This method exists to avoid making get() all + * of virtual, native, and intrinsic candidate. That could have the + * undesirable effect of having the native method used instead of the + * intrinsic when devirtualization fails. + */ + @IntrinsicCandidate + private native T get0(); + /** * Tests if the referent of this reference object is {@code obj}. * Using a {@code null} {@code obj} returns {@code true} if the diff --git a/src/java.base/share/native/libjava/Reference.c b/src/java.base/share/native/libjava/Reference.c index ce5b34299ad..7fef23c2ba8 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, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, 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 @@ -44,6 +44,12 @@ Java_java_lang_ref_Reference_waitForReferencePendingList(JNIEnv *env, jclass ign JVM_WaitForReferencePendingList(env); } +JNIEXPORT jobject JNICALL +Java_java_lang_ref_Reference_get0(JNIEnv *env, jobject ref) +{ + return JVM_ReferenceGet(env, ref); +} + JNIEXPORT jboolean JNICALL Java_java_lang_ref_Reference_refersTo0(JNIEnv *env, jobject ref, jobject o) { diff --git a/test/hotspot/jtreg/gc/TestNativeReferenceGet.java b/test/hotspot/jtreg/gc/TestNativeReferenceGet.java new file mode 100644 index 00000000000..222d250f79a --- /dev/null +++ b/test/hotspot/jtreg/gc/TestNativeReferenceGet.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2025, 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. + */ + +package gc; + +/** + * @test + * @bug 8352565 + * @summary Determine whether the native method implementation of + * Reference.get() works as expected. Disable the intrinsic implementation to + * force use of the native method. + * @library /test/lib + * @modules java.base/java.lang.ref:open + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:DisableIntrinsic=_Reference_get0 + * gc.TestNativeReferenceGet + */ + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import jdk.test.whitebox.WhiteBox; + +public final class TestNativeReferenceGet { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void gcUntilOld(Object o) { + while (!WB.isObjectInOldGen(o)) { + WB.fullGC(); + } + } + + private static final class TestObject { + public final int value; + + public TestObject(int value) { + this.value = value; + } + } + + private static final ReferenceQueue queue = + new ReferenceQueue(); + + private static final class Ref extends WeakReference { + public Ref(TestObject obj) { + super(obj, queue); + } + } + + private static final int NUM_REFS = 100; + + private static List references = null; + private static List referents = null; + + // Create all the objects used by the test, and ensure they are all in the + // old generation. + private static void setup() { + references = new ArrayList(NUM_REFS); + referents = new ArrayList(NUM_REFS); + + for (int i = 0; i < NUM_REFS; ++i) { + TestObject obj = new TestObject(i); + referents.add(obj); + references.add(new Ref(obj)); + } + + gcUntilOld(references); + gcUntilOld(referents); + for (int i = 0; i < NUM_REFS; ++i) { + gcUntilOld(references.get(i)); + gcUntilOld(referents.get(i)); + } + } + + // Discard all the strong references. + private static void dropReferents() { + // Not using List.clear() because it doesn't document null'ing elements. + for (int i = 0; i < NUM_REFS; ++i) { + referents.set(i, null); + } + } + + // Create new strong references from the weak references, by using the + // native method implementation of Reference.get() and recording the value + // in references. + private static void strengthenReferents() { + for (int i = 0; i < NUM_REFS; ++i) { + referents.set(i, references.get(i).get()); + } + } + + private static void check() { + // None of the references should have been cleared and enqueued, + // because we have strong references to all the referents. + try { + while (WB.waitForReferenceProcessing()) {} + } catch (InterruptedException e) { + throw new RuntimeException("Test interrupted"); + } + if (queue.poll() != null) { + throw new RuntimeException("Reference enqueued"); + } + + // Check details of expected state. + for (int i = 0; i < NUM_REFS; ++i) { + Ref reference = (Ref) references.get(i); + TestObject referent = reference.get(); + if (referent == null) { + throw new RuntimeException("Referent not strengthened"); + } else if (referent != referents.get(i)) { + throw new RuntimeException( + "Reference referent differs from saved referent: " + i); + } else if (referent.value != i) { + throw new RuntimeException( + "Referent " + i + " value: " + referent.value); + } + } + } + + private static void testConcurrent() { + System.out.println("Testing concurrent GC"); + try { + WB.concurrentGCAcquireControl(); + dropReferents(); + WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED); + strengthenReferents(); + WB.concurrentGCRunToIdle(); + check(); + } finally { + WB.concurrentGCReleaseControl(); + } + } + + private static void testNonconcurrent() { + System.out.println("Testing nonconcurrent GC"); + // A GC between clearing and strengthening will result in test failure. + // We try to make that unlikely via this immediately preceeding GC. + WB.fullGC(); + dropReferents(); + strengthenReferents(); + WB.fullGC(); + check(); + } + + public static final void main(String[] args) { + setup(); + if (WB.supportsConcurrentGCBreakpoints()) { + testConcurrent(); + } else { + testNonconcurrent(); + } + } +}