8352565: Add native method implementation of Reference.get()

Reviewed-by: vlivanov, tschatzl, lmesnik
This commit is contained in:
Kim Barrett 2025-06-25 18:22:06 +00:00
parent 7447276475
commit 56c75453cd
20 changed files with 236 additions and 40 deletions

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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:

View File

@ -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) \

View File

@ -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") \

View File

@ -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);

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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) {

View File

@ -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:

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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<TestObject> queue =
new ReferenceQueue<TestObject>();
private static final class Ref extends WeakReference<TestObject> {
public Ref(TestObject obj) {
super(obj, queue);
}
}
private static final int NUM_REFS = 100;
private static List<Ref> references = null;
private static List<TestObject> 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<Ref>(NUM_REFS);
referents = new ArrayList<TestObject>(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();
}
}
}