1 Star 0 Fork 81

zhaosai/openjdk-1.8.0

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
8057967-CallSite-dependency-tracking-scales-devastat.patch 33.95 KB
一键复制 编辑 原始数据 按行查看 历史
kuen 提交于 2024-01-18 15:17 . I8X6CH: upgrade to 8u402
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
From 1059c5d5f9d1e50607c726b619f39ac68954bda7 Mon Sep 17 00:00:00 2001
From: zhangyipeng <zhangyipeng7@huawei.com>
Date: Mon, 15 Jan 2024 11:40:07 +0800
Subject: [PATCH] [Backport]8057967: CallSite dependency tracking scales devastatingly
poorly
---
hotspot/src/share/vm/ci/ciCallSite.cpp | 19 +++
hotspot/src/share/vm/ci/ciCallSite.hpp | 1 +
hotspot/src/share/vm/classfile/javaClasses.cpp | 48 +++++-
hotspot/src/share/vm/classfile/javaClasses.hpp | 9 +-
hotspot/src/share/vm/classfile/vmSymbols.hpp | 4 +-
hotspot/src/share/vm/code/dependencies.cpp | 27 ++--
hotspot/src/share/vm/code/dependencies.hpp | 7 +-
hotspot/src/share/vm/memory/universe.cpp | 8 +-
hotspot/src/share/vm/prims/methodHandles.cpp | 50 +++++-
hotspot/src/share/vm/prims/methodHandles.hpp | 3 +
.../compiler/jsr292/CallSiteDepContextTest.java | 179 +++++++++++++++++++++
.../share/classes/java/lang/invoke/CallSite.java | 55 ++++++-
.../java/lang/invoke/MethodHandleNatives.java | 4 +
13 files changed, 385 insertions(+), 29 deletions(-)
create mode 100644 hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
diff --git a/hotspot/src/share/vm/ci/ciCallSite.cpp b/hotspot/src/share/vm/ci/ciCallSite.cpp
index 794042a79..f58346aea 100644
--- a/hotspot/src/share/vm/ci/ciCallSite.cpp
+++ b/hotspot/src/share/vm/ci/ciCallSite.cpp
@@ -49,6 +49,25 @@ ciMethodHandle* ciCallSite::get_target() const {
}
// ------------------------------------------------------------------
+// ciCallSite::get_context
+//
+// Return the target MethodHandle of this CallSite.
+ciKlass* ciCallSite::get_context() {
+ assert(!is_constant_call_site(), "");
+
+ VM_ENTRY_MARK;
+ oop call_site_oop = get_oop();
+ InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site_oop);
+ if (ctxk == NULL) {
+ // The call site doesn't have a context associated. Set it to the default context.
+ oop def_context_oop = java_lang_invoke_CallSite::default_context();
+ java_lang_invoke_CallSite::set_context_cas(call_site_oop, def_context_oop, /*expected=*/NULL);
+ ctxk = MethodHandles::get_call_site_context(call_site_oop);
+ }
+ return (CURRENT_ENV->get_metadata(ctxk))->as_klass();
+}
+
+// ------------------------------------------------------------------
// ciCallSite::print
//
// Print debugging information about the CallSite.
diff --git a/hotspot/src/share/vm/ci/ciCallSite.hpp b/hotspot/src/share/vm/ci/ciCallSite.hpp
index 063f1e3a5..040e894d0 100644
--- a/hotspot/src/share/vm/ci/ciCallSite.hpp
+++ b/hotspot/src/share/vm/ci/ciCallSite.hpp
@@ -43,6 +43,7 @@ public:
// Return the target MethodHandle of this CallSite.
ciMethodHandle* get_target() const;
+ ciKlass* get_context();
void print();
};
diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp
index 9db88611b..fc4165b04 100644
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp
@@ -100,21 +100,22 @@ InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) {
static bool find_field(InstanceKlass* ik,
Symbol* name_symbol, Symbol* signature_symbol,
fieldDescriptor* fd,
- bool allow_super = false) {
- if (allow_super)
- return ik->find_field(name_symbol, signature_symbol, fd) != NULL;
- else
+ bool is_static = false, bool allow_super = false) {
+ if (allow_super || is_static) {
+ return ik->find_field(name_symbol, signature_symbol, is_static, fd) != NULL;
+ } else {
return ik->find_local_field(name_symbol, signature_symbol, fd);
+ }
}
// Helpful routine for computing field offsets at run time rather than hardcoding them
static void
compute_offset(int &dest_offset,
Klass* klass_oop, Symbol* name_symbol, Symbol* signature_symbol,
- bool allow_super = false) {
+ bool is_static = false, bool allow_super = false) {
fieldDescriptor fd;
InstanceKlass* ik = InstanceKlass::cast(klass_oop);
- if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
+ if (!find_field(ik, name_symbol, signature_symbol, &fd, is_static, allow_super)) {
ResourceMark rm;
tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
#ifndef PRODUCT
@@ -3002,15 +3003,50 @@ int java_lang_invoke_MethodType::rtype_slot_count(oop mt) {
// Support for java_lang_invoke_CallSite
int java_lang_invoke_CallSite::_target_offset;
+int java_lang_invoke_CallSite::_context_offset;
+int java_lang_invoke_CallSite::_default_context_offset;
void java_lang_invoke_CallSite::compute_offsets() {
if (!EnableInvokeDynamic) return;
Klass* k = SystemDictionary::CallSite_klass();
if (k != NULL) {
compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_lang_invoke_MethodHandle_signature());
+ compute_offset(_context_offset, k, vmSymbols::context_name(), vmSymbols::sun_misc_Cleaner_signature());
+ compute_offset(_default_context_offset, k,
+ vmSymbols::DEFAULT_CONTEXT_name(), vmSymbols::sun_misc_Cleaner_signature(),
+ /*is_static=*/true, /*allow_super=*/false);
}
}
+oop java_lang_invoke_CallSite::context_volatile(oop call_site) {
+ assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+
+ oop dep_oop = call_site->obj_field_volatile(_context_offset);
+ return dep_oop;
+}
+
+void java_lang_invoke_CallSite::set_context_volatile(oop call_site, oop context) {
+ assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+ call_site->obj_field_put_volatile(_context_offset, context);
+}
+
+bool java_lang_invoke_CallSite::set_context_cas(oop call_site, oop context, oop expected) {
+ assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+ HeapWord* context_addr = call_site->obj_field_addr<HeapWord>(_context_offset);
+ oop res = oopDesc::atomic_compare_exchange_oop(context, context_addr, expected, true);
+ bool success = (res == expected);
+ if (success) {
+ update_barrier_set((void*)context_addr, context);
+ }
+ return success;
+}
+
+oop java_lang_invoke_CallSite::default_context() {
+ InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::CallSite_klass());
+ oop def_context_oop = ik->java_mirror()->obj_field(_default_context_offset);
+ assert(!oopDesc::is_null(def_context_oop), "");
+ return def_context_oop;
+}
// Support for java_security_AccessControlContext
diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp
index d6e288fb8..35934319d 100644
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp
@@ -1212,6 +1212,9 @@ class java_lang_invoke_CallSite: AllStatic {
private:
static int _target_offset;
+ static int _context_offset;
+ static int _default_context_offset;
+
static void compute_offsets();
@@ -1222,6 +1225,11 @@ public:
static volatile oop target_volatile(oop site) { return oop((oopDesc *)(site->obj_field_volatile(_target_offset))); }
static void set_target_volatile(oop site, oop target) { site->obj_field_put_volatile(_target_offset, target); }
+ static oop context_volatile(oop site);
+ static void set_context_volatile(oop site, oop context);
+ static bool set_context_cas (oop site, oop context, oop expected);
+
+ static oop default_context();
// Testers
static bool is_subclass(Klass* klass) {
@@ -1235,7 +1243,6 @@ public:
static int target_offset_in_bytes() { return _target_offset; }
};
-
// Interface to java.security.AccessControlContext objects
class java_security_AccessControlContext: AllStatic {
diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp
index 494fd9bdf..f92b709ed 100644
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp
@@ -301,6 +301,7 @@
template(setTargetNormal_name, "setTargetNormal") \
template(setTargetVolatile_name, "setTargetVolatile") \
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
+ template(DEFAULT_CONTEXT_name, "DEFAULT_CONTEXT") \
NOT_LP64( do_alias(intptr_signature, int_signature) ) \
LP64_ONLY( do_alias(intptr_signature, long_signature) ) \
\
@@ -517,6 +518,7 @@
template(string_signature, "Ljava/lang/String;") \
template(reference_signature, "Ljava/lang/ref/Reference;") \
template(referencequeue_signature, "Ljava/lang/ref/ReferenceQueue;") \
+ template(sun_misc_Cleaner_signature, "Lsun/misc/Cleaner;") \
template(executable_signature, "Ljava/lang/reflect/Executable;") \
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \
@@ -570,7 +572,7 @@
template(createGarbageCollectorMBean_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;") \
template(trigger_name, "trigger") \
template(clear_name, "clear") \
- template(trigger_method_signature, "(ILjava/lang/management/MemoryUsage;)V") \
+ template(trigger_method_signature, "(ILjava/lang/management/MemoryUsage;)V") \
template(startAgent_name, "startAgent") \
template(startRemoteAgent_name, "startRemoteManagementAgent") \
template(startLocalAgent_name, "startLocalManagementAgent") \
diff --git a/hotspot/src/share/vm/code/dependencies.cpp b/hotspot/src/share/vm/code/dependencies.cpp
index d1fe08b54..decbce8be 100644
--- a/hotspot/src/share/vm/code/dependencies.cpp
+++ b/hotspot/src/share/vm/code/dependencies.cpp
@@ -123,8 +123,9 @@ void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) {
}
void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) {
- check_ctxk(call_site->klass());
- assert_common_2(call_site_target_value, call_site, method_handle);
+ ciKlass* ctxk = call_site->get_context();
+ check_ctxk(ctxk);
+ assert_common_3(call_site_target_value, ctxk, call_site, method_handle);
}
// Helper function. If we are adding a new dep. under ctxk2,
@@ -396,7 +397,7 @@ int Dependencies::_dep_args[TYPE_LIMIT] = {
3, // unique_concrete_methods_2 ctxk, m1, m2
2, // unique_implementor ctxk, implementor
1, // no_finalizable_subclasses ctxk
- 2 // call_site_target_value call_site, method_handle
+ 3 // call_site_target_value ctxk, call_site, method_handle
};
const char* Dependencies::dep_name(Dependencies::DepType dept) {
@@ -598,7 +599,7 @@ void Dependencies::DepStream::log_dependency(Klass* witness) {
const int nargs = argument_count();
GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
for (int j = 0; j < nargs; j++) {
- if (type() == call_site_target_value) {
+ if (is_oop_argument(j)) {
args->push(argument_oop(j));
} else {
args->push(argument(j));
@@ -726,7 +727,7 @@ Klass* Dependencies::DepStream::context_type() {
}
// Some dependencies are using the klass of the first object
- // argument as implicit context type (e.g. call_site_target_value).
+ // argument as implicit context type.
{
int ctxkj = dep_implicit_context_arg(type());
if (ctxkj >= 0) {
@@ -1647,9 +1648,16 @@ Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepCh
return find_finalizable_subclass(search_at);
}
-Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
- assert(call_site ->is_a(SystemDictionary::CallSite_klass()), "sanity");
- assert(method_handle->is_a(SystemDictionary::MethodHandle_klass()), "sanity");
+Klass* Dependencies::check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes) {
+ assert(call_site->is_a(SystemDictionary::CallSite_klass()), "sanity");
+ assert(!oopDesc::is_null(method_handle), "sanity");
+
+ Klass* call_site_ctxk = MethodHandles::get_call_site_context(call_site);
+ assert(!Klass::is_null(call_site_ctxk), "call site context should be initialized already");
+ if (recorded_ctxk != call_site_ctxk) {
+ // Stale context
+ return recorded_ctxk;
+ }
if (changes == NULL) {
// Validate all CallSites
if (java_lang_invoke_CallSite::target(call_site) != method_handle)
@@ -1664,7 +1672,6 @@ Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_hand
return NULL; // assertion still valid
}
-
void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
if (witness != NULL) {
if (TraceDependencies) {
@@ -1728,7 +1735,7 @@ Klass* Dependencies::DepStream::check_call_site_dependency(CallSiteDepChange* ch
Klass* witness = NULL;
switch (type()) {
case call_site_target_value:
- witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes);
+ witness = check_call_site_target_value(context_type(), argument_oop(1), argument_oop(2), changes);
break;
default:
witness = NULL;
diff --git a/hotspot/src/share/vm/code/dependencies.hpp b/hotspot/src/share/vm/code/dependencies.hpp
index 0392d4e3d..da2201c3f 100644
--- a/hotspot/src/share/vm/code/dependencies.hpp
+++ b/hotspot/src/share/vm/code/dependencies.hpp
@@ -176,7 +176,7 @@ class Dependencies: public ResourceObj {
klass_types = all_types & ~non_klass_types,
non_ctxk_types = (1 << evol_method),
- implicit_ctxk_types = (1 << call_site_target_value),
+ implicit_ctxk_types = 0,
explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
max_arg_count = 3, // current maximum number of arguments (incl. ctxk)
@@ -340,7 +340,7 @@ class Dependencies: public ResourceObj {
static Klass* check_exclusive_concrete_methods(Klass* ctxk, Method* m1, Method* m2,
KlassDepChange* changes = NULL);
static Klass* check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes = NULL);
- static Klass* check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
+ static Klass* check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
// A returned Klass* is NULL if the dependency assertion is still
// valid. A non-NULL Klass* is a 'witness' to the assertion
// failure, a point in the class hierarchy where the assertion has
@@ -506,6 +506,7 @@ class Dependencies: public ResourceObj {
bool next();
DepType type() { return _type; }
+ bool is_oop_argument(int i) { return type() == call_site_target_value && i > 0; }
int argument_count() { return dep_args(type()); }
int argument_index(int i) { assert(0 <= i && i < argument_count(), "oob");
return _xi[i]; }
@@ -664,7 +665,7 @@ class CallSiteDepChange : public DepChange {
_method_handle(method_handle)
{
assert(_call_site() ->is_a(SystemDictionary::CallSite_klass()), "must be");
- assert(_method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
+ assert(_method_handle.is_null() || _method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
}
// What kind of DepChange is this?
diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp
index 23433d187..7028d378e 100644
--- a/hotspot/src/share/vm/memory/universe.cpp
+++ b/hotspot/src/share/vm/memory/universe.cpp
@@ -56,6 +56,7 @@
#include "oops/oop.inline.hpp"
#include "oops/typeArrayKlass.hpp"
#include "prims/jvmtiRedefineClassesTrace.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/arguments.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/fprofiler.hpp"
@@ -1233,8 +1234,11 @@ void Universe::flush_dependents_on(Handle call_site, Handle method_handle) {
int marked = 0;
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
- InstanceKlass* call_site_klass = InstanceKlass::cast(call_site->klass());
- marked = call_site_klass->mark_dependent_nmethods(changes);
+ InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
+ if (ctxk == NULL) {
+ return; // No dependencies to invalidate yet.
+ }
+ marked = ctxk->mark_dependent_nmethods(changes);
}
if (marked > 0) {
// At least one nmethod has been marked for deoptimization
diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp
index 29598d500..c1cbabec2 100644
--- a/hotspot/src/share/vm/prims/methodHandles.cpp
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp
@@ -946,6 +946,24 @@ int MethodHandles::find_MemberNames(KlassHandle k,
return rfill + overflow;
}
+// Get context class for a CallSite instance: either extract existing context or use default one.
+InstanceKlass* MethodHandles::get_call_site_context(oop call_site) {
+ // In order to extract a context the following traversal is performed:
+ // CallSite.context => Cleaner.referent => Class._klass => Klass
+ assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+ oop context_oop = java_lang_invoke_CallSite::context_volatile(call_site);
+ if (oopDesc::is_null(context_oop)) {
+ return NULL; // The context hasn't been initialized yet.
+ }
+ oop context_class_oop = java_lang_ref_Reference::referent(context_oop);
+ if (oopDesc::is_null(context_class_oop)) {
+ // The context reference was cleared by GC, so current dependency context
+ // isn't usable anymore. Context should be fetched from CallSite again.
+ return NULL;
+ }
+ return InstanceKlass::cast(java_lang_Class::as_Klass(context_class_oop));
+}
+
//------------------------------------------------------------------------------
// MemberNameTable
//
@@ -1305,7 +1323,7 @@ JVM_END
JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
- Handle target (THREAD, JNIHandles::resolve(target_jh));
+ Handle target (THREAD, JNIHandles::resolve_non_null(target_jh));
{
// Walk all nmethods depending on this call site.
MutexLocker mu(Compile_lock, thread);
@@ -1317,7 +1335,7 @@ JVM_END
JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
- Handle target (THREAD, JNIHandles::resolve(target_jh));
+ Handle target (THREAD, JNIHandles::resolve_non_null(target_jh));
{
// Walk all nmethods depending on this call site.
MutexLocker mu(Compile_lock, thread);
@@ -1327,6 +1345,33 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec
}
JVM_END
+JVM_ENTRY(void, MHN_invalidateDependentNMethods(JNIEnv* env, jobject igcls, jobject call_site_jh)) {
+ Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
+ {
+ // Walk all nmethods depending on this call site.
+ MutexLocker mu1(Compile_lock, thread);
+
+ CallSiteDepChange changes(call_site(), Handle());
+
+ InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
+ if (ctxk == NULL) {
+ return; // No dependencies to invalidate yet.
+ }
+ int marked = 0;
+ {
+ MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ marked = ctxk->mark_dependent_nmethods(changes);
+ }
+ java_lang_invoke_CallSite::set_context_volatile(call_site(), NULL); // Reset call site to initial state
+ if (marked > 0) {
+ // At least one nmethod has been marked for deoptimization
+ VM_Deoptimize op;
+ VMThread::execute(&op);
+ }
+ }
+}
+JVM_END
+
/**
* Throws a java/lang/UnsupportedOperationException unconditionally.
* This is required by the specification of MethodHandle.invoke if
@@ -1381,6 +1426,7 @@ static JNINativeMethod MHN_methods[] = {
{CC "objectFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_objectFieldOffset)},
{CC "setCallSiteTargetNormal", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetNormal)},
{CC "setCallSiteTargetVolatile", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetVolatile)},
+ {CC"invalidateDependentNMethods", CC"("CS")V", FN_PTR(MHN_invalidateDependentNMethods)},
{CC "staticFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_staticFieldOffset)},
{CC "staticFieldBase", CC "(" MEM ")" OBJ, FN_PTR(MHN_staticFieldBase)},
{CC "getMemberVMInfo", CC "(" MEM ")" OBJ, FN_PTR(MHN_getMemberVMInfo)}
diff --git a/hotspot/src/share/vm/prims/methodHandles.hpp b/hotspot/src/share/vm/prims/methodHandles.hpp
index db6e06180..4b6af60df 100644
--- a/hotspot/src/share/vm/prims/methodHandles.hpp
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp
@@ -68,6 +68,9 @@ class MethodHandles: AllStatic {
// bit values for suppress argument to expand_MemberName:
enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
+ // CallSite support
+ static InstanceKlass* get_call_site_context(oop call_site);
+
// Generate MethodHandles adapters.
static void generate_adapters();
diff --git a/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
new file mode 100644
index 000000000..11e46ed03
--- /dev/null
+++ b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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 8057967
+ * @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest
+ */
+package java.lang.invoke;
+
+import java.lang.ref.*;
+import jdk.internal.org.objectweb.asm.*;
+import sun.misc.Unsafe;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class CallSiteDepContextTest {
+ static final Unsafe UNSAFE = Unsafe.getUnsafe();
+ static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
+ static final String CLASS_NAME = "java/lang/invoke/Test";
+ static final String METHOD_NAME = "m";
+ static final MethodType TYPE = MethodType.methodType(int.class);
+
+ static MutableCallSite mcs;
+ static MethodHandle bsmMH;
+
+ static {
+ try {
+ bsmMH = LOOKUP.findStatic(
+ CallSiteDepContextTest.class, "bootstrap",
+ MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class));
+ } catch(Throwable e) {
+ throw new InternalError(e);
+ }
+ }
+
+ public static CallSite bootstrap(MethodHandles.Lookup caller,
+ String invokedName,
+ MethodType invokedType) {
+ return mcs;
+ }
+
+ static class T {
+ static int f1() { return 1; }
+ static int f2() { return 2; }
+ }
+
+ static byte[] getClassFile(String suffix) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ MethodVisitor mv;
+ cw.visit(52, ACC_PUBLIC | ACC_SUPER, CLASS_NAME + suffix, null, "java/lang/Object", null);
+ {
+ mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME, TYPE.toMethodDescriptorString(), null, null);
+ mv.visitCode();
+ Handle bsm = new Handle(H_INVOKESTATIC,
+ "java/lang/invoke/CallSiteDepContextTest", "bootstrap",
+ bsmMH.type().toMethodDescriptorString());
+ mv.visitInvokeDynamicInsn("methodName", TYPE.toMethodDescriptorString(), bsm);
+ mv.visitInsn(IRETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ private static void execute(int expected, MethodHandle... mhs) throws Throwable {
+ for (int i = 0; i < 20_000; i++) {
+ for (MethodHandle mh : mhs) {
+ int r = (int) mh.invokeExact();
+ if (r != expected) {
+ throw new Error(r + " != " + expected);
+ }
+ }
+ }
+ }
+
+ public static void testSharedCallSite() throws Throwable {
+ Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
+ Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
+
+ MethodHandle[] mhs = new MethodHandle[] {
+ LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
+ LOOKUP.findStatic(cls2, METHOD_NAME, TYPE)
+ };
+
+ mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+ execute(1, mhs);
+ mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+ execute(2, mhs);
+ }
+
+ public static void testNonBoundCallSite() throws Throwable {
+ mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+
+ // mcs.context == null
+ MethodHandle mh = mcs.dynamicInvoker();
+ execute(1, mh);
+
+ // mcs.context == cls1
+ Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null);
+ MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
+
+ execute(1, mh1);
+
+ mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+
+ execute(2, mh, mh1);
+ }
+
+ static ReferenceQueue rq = new ReferenceQueue();
+ static PhantomReference ref;
+
+ public static void testGC() throws Throwable {
+ mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+
+ Class<?>[] cls = new Class[] {
+ UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1"), null),
+ UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2"), null),
+ };
+
+ MethodHandle[] mhs = new MethodHandle[] {
+ LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE),
+ LOOKUP.findStatic(cls[1], METHOD_NAME, TYPE),
+ };
+
+ // mcs.context == cls[0]
+ int r = (int) mhs[0].invokeExact();
+
+ execute(1, mhs);
+
+ ref = new PhantomReference<>(cls[0], rq);
+ cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3"), null);
+ mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
+
+ do {
+ System.gc();
+ try {
+ Reference ref1 = rq.remove(1000);
+ if (ref1 == ref) {
+ ref1.clear();
+ System.gc(); // Ensure that the stale context is cleared
+ break;
+ }
+ } catch(InterruptedException e) { /* ignore */ }
+ } while (true);
+
+ execute(1, mhs);
+ mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+ execute(2, mhs);
+ }
+
+ public static void main(String[] args) throws Throwable {
+ testSharedCallSite();
+ testNonBoundCallSite();
+ testGC();
+ System.out.println("TEST PASSED");
+ }
+}
diff --git a/jdk/src/share/classes/java/lang/invoke/CallSite.java b/jdk/src/share/classes/java/lang/invoke/CallSite.java
index 10ac1c071..11e452b96 100644
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java
@@ -25,9 +25,10 @@
package java.lang.invoke;
-import sun.invoke.empty.Empty;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import java.lang.reflect.Field;
+import sun.misc.Cleaner;
/**
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
@@ -136,6 +137,50 @@ public class CallSite {
}
/**
+ * {@code CallSite} dependency context.
+ * VM uses context class to store nmethod dependencies on the call site target.
+ * Can be in 2 states: (a) null; or (b) {@code Cleaner} instance pointing to some Class instance.
+ * Lazily initialized when CallSite instance is linked to some indy call site or VM needs
+ * it to store dependencies. As a corollary, "null" context means there are no dependencies
+ * registered yet. {@code Cleaner} is used in 2 roles:
+ * (a) context class access for VM;
+ * (b) stale context class cleanup.
+ * {@code Cleaner} holds the context class until cleanup action is finished (see {@code PhantomReference}).
+ * Though it's impossible to get the context class using {@code Reference.get()}, VM extracts it directly
+ * from {@code Reference.referent} field.
+ */
+ private volatile Cleaner context = null;
+
+ /**
+ * Default context.
+ * VM uses it to initialize non-linked CallSite context.
+ */
+ private static class DefaultContext {}
+ private static final Cleaner DEFAULT_CONTEXT = makeContext(DefaultContext.class, null);
+
+ private static Cleaner makeContext(Class<?> referent, final CallSite holder) {
+ return Cleaner.create(referent,
+ new Runnable() {
+ @Override public void run() {
+ MethodHandleNatives.invalidateDependentNMethods(holder);
+ }
+ });
+ }
+
+ /** Initialize context class used for nmethod dependency tracking */
+ /*package-private*/
+ void initContext(Class<?> newContext) {
+ // If there are concurrent actions, exactly one succeeds.
+ if (context == null) {
+ UNSAFE.compareAndSwapObject(this, CONTEXT_OFFSET, /*expected=*/null, makeContext(newContext, this));
+ // No need to care about failed CAS attempt.
+ // Since initContext is called from indy call site linkage in newContext class, there's no risk
+ // that the context class becomes dead while corresponding context cleaner is alive (causing cleanup
+ // action in the wrong context).
+ }
+ }
+
+ /**
* Returns the type of this call site's target.
* Although targets may change, any call site's type is permanent, and can never change to an unequal type.
* The {@code setTarget} method enforces this invariant by refusing any new target that does
@@ -246,11 +291,13 @@ public class CallSite {
}
// unsafe stuff:
- private static final long TARGET_OFFSET;
+ private static final long TARGET_OFFSET;
+ private static final long CONTEXT_OFFSET;
static {
try {
- TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
- } catch (Exception ex) { throw new Error(ex); }
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ CONTEXT_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("context"));
+ } catch (Exception ex) { throw newInternalError(ex); }
}
/*package-private*/
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
index ecc146078..9a1343c50 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
@@ -71,6 +71,9 @@ class MethodHandleNatives {
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
+ /** Invalidate CallSite context: clean up dependent nmethods and reset call site context to initial state (null). */
+ static native void invalidateDependentNMethods(CallSite site);
+
private static native void registerNatives();
static {
registerNatives();
@@ -314,6 +317,7 @@ class MethodHandleNatives {
return Invokers.linkToTargetMethod(type);
} else {
appendixResult[0] = callSite;
+ callSite.initContext(caller);
return Invokers.linkToCallSiteMethod(type);
}
}
--
2.12.3
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/zhaosai-Simon/openjdk-1.8.0.git
git@gitee.com:zhaosai-Simon/openjdk-1.8.0.git
zhaosai-Simon
openjdk-1.8.0
openjdk-1.8.0
master

搜索帮助