1162 lines
51 KiB
Diff
1162 lines
51 KiB
Diff
From 4ec8d45e1a51fb6c4e407052a52762ad3a59fa60 Mon Sep 17 00:00:00 2001
|
|
From: zhangyipeng <zhangyipeng7@huawei.com>
|
|
Date: Tue, 16 Jan 2024 10:00:09 +0800
|
|
Subject: [PATCH] [Backport]8079205: CallSite dependency tracking is broken after
|
|
sun.misc.Cleaner became automatically cleared
|
|
---
|
|
hotspot/src/share/vm/ci/ciCallSite.cpp | 19 ---
|
|
hotspot/src/share/vm/ci/ciCallSite.hpp | 1 -
|
|
hotspot/src/share/vm/ci/ciInstanceKlass.cpp | 8 +-
|
|
hotspot/src/share/vm/classfile/javaClasses.cpp | 44 +++---
|
|
hotspot/src/share/vm/classfile/javaClasses.hpp | 37 ++++-
|
|
.../src/share/vm/classfile/systemDictionary.hpp | 1 +
|
|
hotspot/src/share/vm/classfile/vmSymbols.hpp | 4 +-
|
|
hotspot/src/share/vm/code/dependencies.cpp | 20 +--
|
|
hotspot/src/share/vm/code/dependencies.hpp | 6 +-
|
|
hotspot/src/share/vm/code/nmethod.cpp | 46 ++++--
|
|
hotspot/src/share/vm/memory/universe.cpp | 34 ----
|
|
hotspot/src/share/vm/memory/universe.hpp | 1 -
|
|
hotspot/src/share/vm/oops/instanceKlass.cpp | 176 ++++++++++++---------
|
|
hotspot/src/share/vm/oops/instanceKlass.hpp | 10 ++
|
|
hotspot/src/share/vm/prims/methodHandles.cpp | 93 +++++++----
|
|
hotspot/src/share/vm/prims/methodHandles.hpp | 5 +-
|
|
.../compiler/jsr292/CallSiteDepContextTest.java | 41 +++--
|
|
.../share/classes/java/lang/invoke/CallSite.java | 44 +-----
|
|
.../java/lang/invoke/MethodHandleNatives.java | 25 ++-
|
|
19 files changed, 335 insertions(+), 280 deletions(-)
|
|
|
|
diff --git a/hotspot/src/share/vm/ci/ciCallSite.cpp b/hotspot/src/share/vm/ci/ciCallSite.cpp
|
|
index f58346aea..794042a79 100644
|
|
--- a/hotspot/src/share/vm/ci/ciCallSite.cpp
|
|
+++ b/hotspot/src/share/vm/ci/ciCallSite.cpp
|
|
@@ -49,25 +49,6 @@ 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 040e894d0..063f1e3a5 100644
|
|
--- a/hotspot/src/share/vm/ci/ciCallSite.hpp
|
|
+++ b/hotspot/src/share/vm/ci/ciCallSite.hpp
|
|
@@ -43,7 +43,6 @@ public:
|
|
|
|
// Return the target MethodHandle of this CallSite.
|
|
ciMethodHandle* get_target() const;
|
|
- ciKlass* get_context();
|
|
|
|
void print();
|
|
};
|
|
diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp
|
|
index 8b17c9b29..876c869d2 100644
|
|
--- a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp
|
|
+++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp
|
|
@@ -485,8 +485,12 @@ int ciInstanceKlass::compute_nonstatic_fields() {
|
|
|
|
if (fields == NULL) {
|
|
// This can happen if this class (java.lang.Class) has invisible fields.
|
|
- _nonstatic_fields = super_fields;
|
|
- return super_fields->length();
|
|
+ if (super_fields != NULL) {
|
|
+ _nonstatic_fields = super_fields;
|
|
+ return super_fields->length();
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
}
|
|
|
|
int flen = fields->length();
|
|
diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp
|
|
index fc4165b04..267bbacd3 100644
|
|
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp
|
|
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp
|
|
@@ -3004,48 +3004,43 @@ int java_lang_invoke_MethodType::rtype_slot_count(oop mt) {
|
|
|
|
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);
|
|
+ compute_offset(_context_offset, k, vmSymbols::context_name(),
|
|
+ vmSymbols::java_lang_invoke_MethodHandleNatives_CallSiteContext_signature());
|
|
}
|
|
}
|
|
|
|
-oop java_lang_invoke_CallSite::context_volatile(oop call_site) {
|
|
+oop java_lang_invoke_CallSite::context(oop call_site) {
|
|
assert(java_lang_invoke_CallSite::is_instance(call_site), "");
|
|
|
|
- oop dep_oop = call_site->obj_field_volatile(_context_offset);
|
|
+ oop dep_oop = call_site->obj_field(_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);
|
|
-}
|
|
+// Support for java_lang_invoke_MethodHandleNatives_CallSiteContext
|
|
|
|
-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);
|
|
+int java_lang_invoke_MethodHandleNatives_CallSiteContext::_vmdependencies_offset;
|
|
+
|
|
+void java_lang_invoke_MethodHandleNatives_CallSiteContext::compute_offsets() {
|
|
+ Klass* k = SystemDictionary::Context_klass();
|
|
+ if (k != NULL) {
|
|
+ CALLSITECONTEXT_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
|
|
}
|
|
- 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;
|
|
+nmethodBucket* java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) {
|
|
+ assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), "");
|
|
+ return (nmethodBucket*) (address) call_site->long_field(_vmdependencies_offset);
|
|
+}
|
|
+
|
|
+void java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(oop call_site, nmethodBucket* context) {
|
|
+ assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), "");
|
|
+ call_site->long_field_put(_vmdependencies_offset, (jlong) (address) context);
|
|
}
|
|
|
|
// Support for java_security_AccessControlContext
|
|
@@ -3441,6 +3436,7 @@ void JavaClasses::compute_offsets() {
|
|
java_lang_invoke_LambdaForm::compute_offsets();
|
|
java_lang_invoke_MethodType::compute_offsets();
|
|
java_lang_invoke_CallSite::compute_offsets();
|
|
+ java_lang_invoke_MethodHandleNatives_CallSiteContext::compute_offsets();
|
|
}
|
|
java_security_AccessControlContext::compute_offsets();
|
|
// Initialize reflection classes. The layouts of these classes
|
|
diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp
|
|
index 35934319d..1eb04b968 100644
|
|
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp
|
|
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp
|
|
@@ -1213,8 +1213,6 @@ class java_lang_invoke_CallSite: AllStatic {
|
|
private:
|
|
static int _target_offset;
|
|
static int _context_offset;
|
|
- static int _default_context_offset;
|
|
-
|
|
|
|
static void compute_offsets();
|
|
|
|
@@ -1225,11 +1223,8 @@ 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();
|
|
+ static oop context(oop site);
|
|
|
|
// Testers
|
|
static bool is_subclass(Klass* klass) {
|
|
@@ -1243,6 +1238,33 @@ public:
|
|
static int target_offset_in_bytes() { return _target_offset; }
|
|
};
|
|
|
|
+// Interface to java.lang.invoke.MethodHandleNatives$CallSiteContext objects
|
|
+
|
|
+#define CALLSITECONTEXT_INJECTED_FIELDS(macro) \
|
|
+ macro(java_lang_invoke_MethodHandleNatives_CallSiteContext, vmdependencies, intptr_signature, false)
|
|
+
|
|
+class java_lang_invoke_MethodHandleNatives_CallSiteContext : AllStatic {
|
|
+ friend class JavaClasses;
|
|
+
|
|
+private:
|
|
+ static int _vmdependencies_offset;
|
|
+
|
|
+ static void compute_offsets();
|
|
+
|
|
+public:
|
|
+ // Accessors
|
|
+ static nmethodBucket* vmdependencies(oop context);
|
|
+ static void set_vmdependencies(oop context, nmethodBucket* bucket);
|
|
+
|
|
+ // Testers
|
|
+ static bool is_subclass(Klass* klass) {
|
|
+ return klass->is_subclass_of(SystemDictionary::Context_klass());
|
|
+ }
|
|
+ static inline bool is_instance(oop obj) {
|
|
+ return obj != NULL && is_subclass(obj->klass());
|
|
+ }
|
|
+};
|
|
+
|
|
// Interface to java.security.AccessControlContext objects
|
|
|
|
class java_security_AccessControlContext: AllStatic {
|
|
@@ -1454,7 +1476,8 @@ class InjectedField {
|
|
#define ALL_INJECTED_FIELDS(macro) \
|
|
CLASS_INJECTED_FIELDS(macro) \
|
|
CLASSLOADER_INJECTED_FIELDS(macro) \
|
|
- MEMBERNAME_INJECTED_FIELDS(macro)
|
|
+ MEMBERNAME_INJECTED_FIELDS(macro) \
|
|
+ CALLSITECONTEXT_INJECTED_FIELDS(macro)
|
|
|
|
// Interface to hard-coded offset checking
|
|
|
|
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
|
index 83ca3794b..ca91f53e7 100644
|
|
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
|
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
|
@@ -159,6 +159,7 @@ class SymbolPropertyTable;
|
|
do_klass(MethodType_klass, java_lang_invoke_MethodType, Pre_JSR292 ) \
|
|
do_klass(BootstrapMethodError_klass, java_lang_BootstrapMethodError, Pre_JSR292 ) \
|
|
do_klass(CallSite_klass, java_lang_invoke_CallSite, Pre_JSR292 ) \
|
|
+ do_klass(Context_klass, java_lang_invoke_MethodHandleNatives_CallSiteContext, Pre ) \
|
|
do_klass(ConstantCallSite_klass, java_lang_invoke_ConstantCallSite, Pre_JSR292 ) \
|
|
do_klass(MutableCallSite_klass, java_lang_invoke_MutableCallSite, Pre_JSR292 ) \
|
|
do_klass(VolatileCallSite_klass, java_lang_invoke_VolatileCallSite, Pre_JSR292 ) \
|
|
diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp
|
|
index f92b709ed..79f15589f 100644
|
|
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp
|
|
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp
|
|
@@ -282,6 +282,7 @@
|
|
/* internal classes known only to the JVM: */ \
|
|
template(java_lang_invoke_MemberName, "java/lang/invoke/MemberName") \
|
|
template(java_lang_invoke_MethodHandleNatives, "java/lang/invoke/MethodHandleNatives") \
|
|
+ template(java_lang_invoke_MethodHandleNatives_CallSiteContext, "java/lang/invoke/MethodHandleNatives$CallSiteContext") \
|
|
template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \
|
|
template(java_lang_invoke_ForceInline_signature, "Ljava/lang/invoke/ForceInline;") \
|
|
template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \
|
|
@@ -289,6 +290,7 @@
|
|
template(java_lang_invoke_Stable_signature, "Ljava/lang/invoke/Stable;") \
|
|
template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \
|
|
template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \
|
|
+ template(java_lang_invoke_MethodHandleNatives_CallSiteContext_signature, "Ljava/lang/invoke/MethodHandleNatives$CallSiteContext;") \
|
|
/* internal up-calls made only by the JVM, via class sun.invoke.MethodHandleNatives: */ \
|
|
template(findMethodHandleType_name, "findMethodHandleType") \
|
|
template(findMethodHandleType_signature, "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;") \
|
|
@@ -411,7 +413,7 @@
|
|
template(init_lock_name, "init_lock") \
|
|
template(signers_name, "signers_name") \
|
|
template(loader_data_name, "loader_data") \
|
|
- template(dependencies_name, "dependencies") \
|
|
+ template(vmdependencies_name, "vmdependencies") \
|
|
template(input_stream_void_signature, "(Ljava/io/InputStream;)V") \
|
|
template(getFileURL_name, "getFileURL") \
|
|
template(getFileURL_signature, "(Ljava/io/File;)Ljava/net/URL;") \
|
|
diff --git a/hotspot/src/share/vm/code/dependencies.cpp b/hotspot/src/share/vm/code/dependencies.cpp
|
|
index decbce8be..52ed2f836 100644
|
|
--- a/hotspot/src/share/vm/code/dependencies.cpp
|
|
+++ b/hotspot/src/share/vm/code/dependencies.cpp
|
|
@@ -123,9 +123,7 @@ void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) {
|
|
}
|
|
|
|
void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) {
|
|
- ciKlass* ctxk = call_site->get_context();
|
|
- check_ctxk(ctxk);
|
|
- assert_common_3(call_site_target_value, ctxk, call_site, method_handle);
|
|
+ assert_common_2(call_site_target_value, call_site, method_handle);
|
|
}
|
|
|
|
// Helper function. If we are adding a new dep. under ctxk2,
|
|
@@ -181,7 +179,6 @@ void Dependencies::assert_common_2(DepType dept,
|
|
}
|
|
}
|
|
} else {
|
|
- assert(dep_implicit_context_arg(dept) == 0, "sanity");
|
|
if (note_dep_seen(dept, x0) && note_dep_seen(dept, x1)) {
|
|
// look in this bucket for redundant assertions
|
|
const int stride = 2;
|
|
@@ -397,7 +394,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
|
|
- 3 // call_site_target_value ctxk, call_site, method_handle
|
|
+ 2 // call_site_target_value call_site, method_handle
|
|
};
|
|
|
|
const char* Dependencies::dep_name(Dependencies::DepType dept) {
|
|
@@ -1648,16 +1645,11 @@ Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepCh
|
|
return find_finalizable_subclass(search_at);
|
|
}
|
|
|
|
-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");
|
|
+Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
|
|
+ assert(!oopDesc::is_null(call_site), "sanity");
|
|
assert(!oopDesc::is_null(method_handle), "sanity");
|
|
+ assert(call_site->is_a(SystemDictionary::CallSite_klass()), "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)
|
|
@@ -1735,7 +1727,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(context_type(), argument_oop(1), argument_oop(2), changes);
|
|
+ witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes);
|
|
break;
|
|
default:
|
|
witness = NULL;
|
|
diff --git a/hotspot/src/share/vm/code/dependencies.hpp b/hotspot/src/share/vm/code/dependencies.hpp
|
|
index da2201c3f..bbe140390 100644
|
|
--- a/hotspot/src/share/vm/code/dependencies.hpp
|
|
+++ b/hotspot/src/share/vm/code/dependencies.hpp
|
|
@@ -175,7 +175,7 @@ class Dependencies: public ResourceObj {
|
|
non_klass_types = (1 << call_site_target_value),
|
|
klass_types = all_types & ~non_klass_types,
|
|
|
|
- non_ctxk_types = (1 << evol_method),
|
|
+ non_ctxk_types = (1 << evol_method) | (1 << call_site_target_value),
|
|
implicit_ctxk_types = 0,
|
|
explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
|
|
|
|
@@ -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(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
|
|
+ static Klass* check_call_site_target_value(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,7 +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; }
|
|
+ bool is_oop_argument(int i) { return type() == call_site_target_value; }
|
|
int argument_count() { return dep_args(type()); }
|
|
int argument_index(int i) { assert(0 <= i && i < argument_count(), "oob");
|
|
return _xi[i]; }
|
|
diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp
|
|
index 01e878022..ba5116575 100644
|
|
--- a/hotspot/src/share/vm/code/nmethod.cpp
|
|
+++ b/hotspot/src/share/vm/code/nmethod.cpp
|
|
@@ -629,13 +629,18 @@ nmethod* nmethod::new_nmethod(methodHandle method,
|
|
// the number of methods compiled. For applications with a lot
|
|
// classes the slow way is too slow.
|
|
for (Dependencies::DepStream deps(nm); deps.next(); ) {
|
|
- Klass* klass = deps.context_type();
|
|
- if (klass == NULL) {
|
|
- continue; // ignore things like evol_method
|
|
+ if (deps.type() == Dependencies::call_site_target_value) {
|
|
+ // CallSite dependencies are managed on per-CallSite instance basis.
|
|
+ oop call_site = deps.argument_oop(0);
|
|
+ MethodHandles::add_dependent_nmethod(call_site, nm);
|
|
+ } else {
|
|
+ Klass* klass = deps.context_type();
|
|
+ if (klass == NULL) {
|
|
+ continue; // ignore things like evol_method
|
|
+ }
|
|
+ // record this nmethod as dependent on this klass
|
|
+ InstanceKlass::cast(klass)->add_dependent_nmethod(nm);
|
|
}
|
|
-
|
|
- // record this nmethod as dependent on this klass
|
|
- InstanceKlass::cast(klass)->add_dependent_nmethod(nm);
|
|
}
|
|
NOT_PRODUCT(nmethod_stats.note_nmethod(nm));
|
|
if (PrintAssembly || CompilerOracle::has_option_string(method, "PrintAssembly")) {
|
|
@@ -1617,17 +1622,24 @@ void nmethod::flush_dependencies(BoolObjectClosure* is_alive) {
|
|
if (!has_flushed_dependencies()) {
|
|
set_has_flushed_dependencies();
|
|
for (Dependencies::DepStream deps(this); deps.next(); ) {
|
|
- Klass* klass = deps.context_type();
|
|
- if (klass == NULL) continue; // ignore things like evol_method
|
|
-
|
|
- // During GC the is_alive closure is non-NULL, and is used to
|
|
- // determine liveness of dependees that need to be updated.
|
|
- if (is_alive == NULL || klass->is_loader_alive(is_alive)) {
|
|
- // The GC defers deletion of this entry, since there might be multiple threads
|
|
- // iterating over the _dependencies graph. Other call paths are single-threaded
|
|
- // and may delete it immediately.
|
|
- bool delete_immediately = is_alive == NULL;
|
|
- InstanceKlass::cast(klass)->remove_dependent_nmethod(this, delete_immediately);
|
|
+ if (deps.type() == Dependencies::call_site_target_value) {
|
|
+ // CallSite dependencies are managed on per-CallSite instance basis.
|
|
+ oop call_site = deps.argument_oop(0);
|
|
+ MethodHandles::remove_dependent_nmethod(call_site, this);
|
|
+ } else {
|
|
+ Klass* klass = deps.context_type();
|
|
+ if (klass == NULL) {
|
|
+ continue; // ignore things like evol_method
|
|
+ }
|
|
+ // During GC the is_alive closure is non-NULL, and is used to
|
|
+ // determine liveness of dependees that need to be updated.
|
|
+ if (is_alive == NULL || klass->is_loader_alive(is_alive)) {
|
|
+ // The GC defers deletion of this entry, since there might be multiple threads
|
|
+ // iterating over the _dependencies graph. Other call paths are single-threaded
|
|
+ // and may delete it immediately.
|
|
+ bool delete_immediately = is_alive == NULL;
|
|
+ InstanceKlass::cast(klass)->remove_dependent_nmethod(this, delete_immediately);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp
|
|
index 7028d378e..d76a10d14 100644
|
|
--- a/hotspot/src/share/vm/memory/universe.cpp
|
|
+++ b/hotspot/src/share/vm/memory/universe.cpp
|
|
@@ -1213,40 +1213,6 @@ void Universe::flush_dependents_on(instanceKlassHandle dependee) {
|
|
}
|
|
}
|
|
|
|
-// Flushes compiled methods dependent on a particular CallSite
|
|
-// instance when its target is different than the given MethodHandle.
|
|
-void Universe::flush_dependents_on(Handle call_site, Handle method_handle) {
|
|
- assert_lock_strong(Compile_lock);
|
|
-
|
|
- if (CodeCache::number_of_nmethods_with_dependencies() == 0) return;
|
|
-
|
|
- // CodeCache can only be updated by a thread_in_VM and they will all be
|
|
- // stopped dring the safepoint so CodeCache will be safe to update without
|
|
- // holding the CodeCache_lock.
|
|
-
|
|
- CallSiteDepChange changes(call_site(), method_handle());
|
|
-
|
|
- // Compute the dependent nmethods that have a reference to a
|
|
- // CallSite object. We use InstanceKlass::mark_dependent_nmethod
|
|
- // directly instead of CodeCache::mark_for_deoptimization because we
|
|
- // want dependents on the call site class only not all classes in
|
|
- // the ContextStream.
|
|
- int marked = 0;
|
|
- {
|
|
- MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
|
- 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
|
|
- VM_Deoptimize op;
|
|
- VMThread::execute(&op);
|
|
- }
|
|
-}
|
|
-
|
|
#ifdef HOTSWAP
|
|
// Flushes compiled methods dependent on dependee in the evolutionary sense
|
|
void Universe::flush_evol_dependents_on(instanceKlassHandle ev_k_h) {
|
|
diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp
|
|
index dbba5a021..88ad002fa 100644
|
|
--- a/hotspot/src/share/vm/memory/universe.hpp
|
|
+++ b/hotspot/src/share/vm/memory/universe.hpp
|
|
@@ -475,7 +475,6 @@ class Universe: AllStatic {
|
|
|
|
// Flushing and deoptimization
|
|
static void flush_dependents_on(instanceKlassHandle dependee);
|
|
- static void flush_dependents_on(Handle call_site, Handle method_handle);
|
|
#ifdef HOTSWAP
|
|
// Flushing and deoptimization in case of evolution
|
|
static void flush_evol_dependents_on(instanceKlassHandle dependee);
|
|
diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
index 00aea4377..ce297b681 100644
|
|
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
@@ -2102,11 +2102,10 @@ int nmethodBucket::decrement() {
|
|
// are dependent on the changes that were passed in and mark them for
|
|
// deoptimization. Returns the number of nmethods found.
|
|
//
|
|
-int InstanceKlass::mark_dependent_nmethods(DepChange& changes) {
|
|
+int nmethodBucket::mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes) {
|
|
assert_locked_or_safepoint(CodeCache_lock);
|
|
int found = 0;
|
|
- nmethodBucket* b = _dependencies;
|
|
- while (b != NULL) {
|
|
+ for (nmethodBucket* b = deps; b != NULL; b = b->next()) {
|
|
nmethod* nm = b->get_nmethod();
|
|
// since dependencies aren't removed until an nmethod becomes a zombie,
|
|
// the dependency list may contain nmethods which aren't alive.
|
|
@@ -2114,7 +2113,6 @@ int InstanceKlass::mark_dependent_nmethods(DepChange& changes) {
|
|
if (TraceDependencies) {
|
|
ResourceMark rm;
|
|
tty->print_cr("Marked for deoptimization");
|
|
- tty->print_cr(" context = %s", this->external_name());
|
|
changes.print();
|
|
nm->print();
|
|
nm->print_dependencies();
|
|
@@ -2122,115 +2120,102 @@ int InstanceKlass::mark_dependent_nmethods(DepChange& changes) {
|
|
nm->mark_for_deoptimization();
|
|
found++;
|
|
}
|
|
- b = b->next();
|
|
}
|
|
return found;
|
|
}
|
|
|
|
-void InstanceKlass::clean_dependent_nmethods() {
|
|
- assert_locked_or_safepoint(CodeCache_lock);
|
|
-
|
|
- if (has_unloaded_dependent()) {
|
|
- nmethodBucket* b = _dependencies;
|
|
- nmethodBucket* last = NULL;
|
|
- while (b != NULL) {
|
|
- assert(b->count() >= 0, err_msg("bucket count: %d", b->count()));
|
|
-
|
|
- nmethodBucket* next = b->next();
|
|
-
|
|
- if (b->count() == 0) {
|
|
- if (last == NULL) {
|
|
- _dependencies = next;
|
|
- } else {
|
|
- last->set_next(next);
|
|
- }
|
|
- delete b;
|
|
- // last stays the same.
|
|
- } else {
|
|
- last = b;
|
|
- }
|
|
-
|
|
- b = next;
|
|
- }
|
|
- set_has_unloaded_dependent(false);
|
|
- }
|
|
-#ifdef ASSERT
|
|
- else {
|
|
- // Verification
|
|
- for (nmethodBucket* b = _dependencies; b != NULL; b = b->next()) {
|
|
- assert(b->count() >= 0, err_msg("bucket count: %d", b->count()));
|
|
- assert(b->count() != 0, "empty buckets need to be cleaned");
|
|
- }
|
|
- }
|
|
-#endif
|
|
-}
|
|
-
|
|
//
|
|
// Add an nmethodBucket to the list of dependencies for this nmethod.
|
|
// It's possible that an nmethod has multiple dependencies on this klass
|
|
// so a count is kept for each bucket to guarantee that creation and
|
|
-// deletion of dependencies is consistent.
|
|
+// deletion of dependencies is consistent. Returns new head of the list.
|
|
//
|
|
-void InstanceKlass::add_dependent_nmethod(nmethod* nm) {
|
|
+nmethodBucket* nmethodBucket::add_dependent_nmethod(nmethodBucket* deps, nmethod* nm) {
|
|
assert_locked_or_safepoint(CodeCache_lock);
|
|
- nmethodBucket* b = _dependencies;
|
|
- nmethodBucket* last = NULL;
|
|
- while (b != NULL) {
|
|
+ for (nmethodBucket* b = deps; b != NULL; b = b->next()) {
|
|
if (nm == b->get_nmethod()) {
|
|
b->increment();
|
|
- return;
|
|
+ return deps;
|
|
}
|
|
- b = b->next();
|
|
}
|
|
- _dependencies = new nmethodBucket(nm, _dependencies);
|
|
+ return new nmethodBucket(nm, deps);
|
|
}
|
|
|
|
-
|
|
//
|
|
// Decrement count of the nmethod in the dependency list and remove
|
|
-// the bucket competely when the count goes to 0. This method must
|
|
+// the bucket completely when the count goes to 0. This method must
|
|
// find a corresponding bucket otherwise there's a bug in the
|
|
-// recording of dependecies.
|
|
-//
|
|
-void InstanceKlass::remove_dependent_nmethod(nmethod* nm, bool delete_immediately) {
|
|
+// recording of dependencies. Returns true if the bucket was deleted,
|
|
+// or marked ready for reclaimation.
|
|
+bool nmethodBucket::remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately) {
|
|
assert_locked_or_safepoint(CodeCache_lock);
|
|
- nmethodBucket* b = _dependencies;
|
|
+
|
|
+ nmethodBucket* first = *deps;
|
|
nmethodBucket* last = NULL;
|
|
- while (b != NULL) {
|
|
+ for (nmethodBucket* b = first; b != NULL; b = b->next()) {
|
|
if (nm == b->get_nmethod()) {
|
|
int val = b->decrement();
|
|
guarantee(val >= 0, err_msg("Underflow: %d", val));
|
|
if (val == 0) {
|
|
if (delete_immediately) {
|
|
if (last == NULL) {
|
|
- _dependencies = b->next();
|
|
+ *deps = b->next();
|
|
} else {
|
|
last->set_next(b->next());
|
|
}
|
|
delete b;
|
|
- } else {
|
|
- // The deletion of this entry is deferred until a later, potentially parallel GC phase.
|
|
- set_has_unloaded_dependent(true);
|
|
}
|
|
}
|
|
- return;
|
|
+ return true;
|
|
}
|
|
last = b;
|
|
- b = b->next();
|
|
}
|
|
+
|
|
#ifdef ASSERT
|
|
- tty->print_cr("### %s can't find dependent nmethod:", this->external_name());
|
|
+ tty->print_raw_cr("### can't find dependent nmethod");
|
|
nm->print();
|
|
#endif // ASSERT
|
|
ShouldNotReachHere();
|
|
+ return false;
|
|
}
|
|
|
|
+// Convenience overload, for callers that don't want to delete the nmethodBucket entry.
|
|
+bool nmethodBucket::remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm) {
|
|
+ nmethodBucket** deps_addr = &deps;
|
|
+ return remove_dependent_nmethod(deps_addr, nm, false /* Don't delete */);
|
|
+}
|
|
+
|
|
+//
|
|
+// Reclaim all unused buckets. Returns new head of the list.
|
|
+//
|
|
+nmethodBucket* nmethodBucket::clean_dependent_nmethods(nmethodBucket* deps) {
|
|
+ nmethodBucket* first = deps;
|
|
+ nmethodBucket* last = NULL;
|
|
+ nmethodBucket* b = first;
|
|
+
|
|
+ while (b != NULL) {
|
|
+ assert(b->count() >= 0, err_msg("bucket count: %d", b->count()));
|
|
+ nmethodBucket* next = b->next();
|
|
+ if (b->count() == 0) {
|
|
+ if (last == NULL) {
|
|
+ first = next;
|
|
+ } else {
|
|
+ last->set_next(next);
|
|
+ }
|
|
+ delete b;
|
|
+ // last stays the same.
|
|
+ } else {
|
|
+ last = b;
|
|
+ }
|
|
+ b = next;
|
|
+ }
|
|
+ return first;
|
|
+}
|
|
|
|
#ifndef PRODUCT
|
|
-void InstanceKlass::print_dependent_nmethods(bool verbose) {
|
|
- nmethodBucket* b = _dependencies;
|
|
+void nmethodBucket::print_dependent_nmethods(nmethodBucket* deps, bool verbose) {
|
|
int idx = 0;
|
|
- while (b != NULL) {
|
|
+ for (nmethodBucket* b = deps; b != NULL; b = b->next()) {
|
|
nmethod* nm = b->get_nmethod();
|
|
tty->print("[%d] count=%d { ", idx++, b->count());
|
|
if (!verbose) {
|
|
@@ -2241,14 +2226,11 @@ void InstanceKlass::print_dependent_nmethods(bool verbose) {
|
|
nm->print_dependencies();
|
|
tty->print_cr("--- } ");
|
|
}
|
|
- b = b->next();
|
|
}
|
|
}
|
|
|
|
-
|
|
-bool InstanceKlass::is_dependent_nmethod(nmethod* nm) {
|
|
- nmethodBucket* b = _dependencies;
|
|
- while (b != NULL) {
|
|
+bool nmethodBucket::is_dependent_nmethod(nmethodBucket* deps, nmethod* nm) {
|
|
+ for (nmethodBucket* b = deps; b != NULL; b = b->next()) {
|
|
if (nm == b->get_nmethod()) {
|
|
#ifdef ASSERT
|
|
int count = b->count();
|
|
@@ -2256,12 +2238,58 @@ bool InstanceKlass::is_dependent_nmethod(nmethod* nm) {
|
|
#endif
|
|
return true;
|
|
}
|
|
- b = b->next();
|
|
}
|
|
return false;
|
|
}
|
|
#endif //PRODUCT
|
|
|
|
+int InstanceKlass::mark_dependent_nmethods(DepChange& changes) {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+ return nmethodBucket::mark_dependent_nmethods(_dependencies, changes);
|
|
+}
|
|
+
|
|
+void InstanceKlass::clean_dependent_nmethods() {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+
|
|
+ if (has_unloaded_dependent()) {
|
|
+ _dependencies = nmethodBucket::clean_dependent_nmethods(_dependencies);
|
|
+ set_has_unloaded_dependent(false);
|
|
+ }
|
|
+#ifdef ASSERT
|
|
+ else {
|
|
+ // Verification
|
|
+ for (nmethodBucket* b = _dependencies; b != NULL; b = b->next()) {
|
|
+ assert(b->count() >= 0, err_msg("bucket count: %d", b->count()));
|
|
+ assert(b->count() != 0, "empty buckets need to be cleaned");
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+void InstanceKlass::add_dependent_nmethod(nmethod* nm) {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+ _dependencies = nmethodBucket::add_dependent_nmethod(_dependencies, nm);
|
|
+}
|
|
+
|
|
+void InstanceKlass::remove_dependent_nmethod(nmethod* nm, bool delete_immediately) {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+
|
|
+ if (nmethodBucket::remove_dependent_nmethod(&_dependencies, nm, delete_immediately)) {
|
|
+ set_has_unloaded_dependent(true);
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifndef PRODUCT
|
|
+void InstanceKlass::print_dependent_nmethods(bool verbose) {
|
|
+ nmethodBucket::print_dependent_nmethods(_dependencies, verbose);
|
|
+}
|
|
+
|
|
+
|
|
+bool InstanceKlass::is_dependent_nmethod(nmethod* nm) {
|
|
+ return nmethodBucket::is_dependent_nmethod(_dependencies, nm);
|
|
+}
|
|
+#endif //PRODUCT
|
|
+
|
|
|
|
// Garbage collection
|
|
|
|
diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
index 973480341..9750ae56d 100644
|
|
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
@@ -1288,6 +1288,16 @@ class nmethodBucket: public CHeapObj<mtClass> {
|
|
nmethodBucket* next() { return _next; }
|
|
void set_next(nmethodBucket* b) { _next = b; }
|
|
nmethod* get_nmethod() { return _nmethod; }
|
|
+
|
|
+ static int mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes);
|
|
+ static nmethodBucket* add_dependent_nmethod(nmethodBucket* deps, nmethod* nm);
|
|
+ static bool remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately);
|
|
+ static bool remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm);
|
|
+ static nmethodBucket* clean_dependent_nmethods(nmethodBucket* deps);
|
|
+#ifndef PRODUCT
|
|
+ static void print_dependent_nmethods(nmethodBucket* deps, bool verbose);
|
|
+ static bool is_dependent_nmethod(nmethodBucket* deps, nmethod* nm);
|
|
+#endif //PRODUCT
|
|
};
|
|
|
|
// An iterator that's used to access the inner classes indices in the
|
|
diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp
|
|
index c1cbabec2..abd7c0b42 100644
|
|
--- a/hotspot/src/share/vm/prims/methodHandles.cpp
|
|
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp
|
|
@@ -946,22 +946,56 @@ 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.
|
|
+void MethodHandles::add_dependent_nmethod(oop call_site, nmethod* nm) {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+
|
|
+ oop context = java_lang_invoke_CallSite::context(call_site);
|
|
+ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context);
|
|
+
|
|
+ nmethodBucket* new_deps = nmethodBucket::add_dependent_nmethod(deps, nm);
|
|
+ if (deps != new_deps) {
|
|
+ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps);
|
|
}
|
|
- 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;
|
|
+}
|
|
+
|
|
+void MethodHandles::remove_dependent_nmethod(oop call_site, nmethod* nm) {
|
|
+ assert_locked_or_safepoint(CodeCache_lock);
|
|
+
|
|
+ oop context = java_lang_invoke_CallSite::context(call_site);
|
|
+ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context);
|
|
+
|
|
+ if (nmethodBucket::remove_dependent_nmethod(deps, nm)) {
|
|
+ nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps);
|
|
+ if (deps != new_deps) {
|
|
+ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) {
|
|
+ assert_lock_strong(Compile_lock);
|
|
+
|
|
+ int marked = 0;
|
|
+ CallSiteDepChange changes(call_site(), target());
|
|
+ {
|
|
+ MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
|
+
|
|
+ oop context = java_lang_invoke_CallSite::context(call_site());
|
|
+ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context);
|
|
+
|
|
+ marked = nmethodBucket::mark_dependent_nmethods(deps, changes);
|
|
+ if (marked > 0) {
|
|
+ nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps);
|
|
+ if (deps != new_deps) {
|
|
+ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (marked > 0) {
|
|
+ // At least one nmethod has been marked for deoptimization
|
|
+ VM_Deoptimize op;
|
|
+ VMThread::execute(&op);
|
|
}
|
|
- return InstanceKlass::cast(java_lang_Class::as_Klass(context_class_oop));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
@@ -1327,7 +1361,7 @@ JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject
|
|
{
|
|
// Walk all nmethods depending on this call site.
|
|
MutexLocker mu(Compile_lock, thread);
|
|
- Universe::flush_dependents_on(call_site, target);
|
|
+ MethodHandles::flush_dependent_nmethods(call_site, target);
|
|
java_lang_invoke_CallSite::set_target(call_site(), target());
|
|
}
|
|
}
|
|
@@ -1339,30 +1373,34 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec
|
|
{
|
|
// Walk all nmethods depending on this call site.
|
|
MutexLocker mu(Compile_lock, thread);
|
|
- Universe::flush_dependents_on(call_site, target);
|
|
+ MethodHandles::flush_dependent_nmethods(call_site, target);
|
|
java_lang_invoke_CallSite::set_target_volatile(call_site(), target());
|
|
}
|
|
}
|
|
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));
|
|
+JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) {
|
|
+ Handle context(THREAD, JNIHandles::resolve_non_null(context_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);
|
|
+ nmethodBucket* b = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context());
|
|
+ while(b != NULL) {
|
|
+ nmethod* nm = b->get_nmethod();
|
|
+ if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) {
|
|
+ nm->mark_for_deoptimization();
|
|
+ marked++;
|
|
+ }
|
|
+ nmethodBucket* next = b->next();
|
|
+ delete b;
|
|
+ b = next;
|
|
+ }
|
|
+ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context(), NULL); // reset context
|
|
}
|
|
- 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;
|
|
@@ -1408,6 +1446,7 @@ JVM_END
|
|
#define MT JLINV "MethodType;"
|
|
#define MH JLINV "MethodHandle;"
|
|
#define MEM JLINV "MemberName;"
|
|
+#define CTX JLINV"MethodHandleNatives$CallSiteContext;"
|
|
|
|
#define CC (char*) /*cast a literal from (const char*)*/
|
|
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
|
|
@@ -1426,7 +1465,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"clearCallSiteContext", CC "(" CTX ")V", FN_PTR(MHN_clearCallSiteContext)},
|
|
{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 4b6af60df..71508d215 100644
|
|
--- a/hotspot/src/share/vm/prims/methodHandles.hpp
|
|
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp
|
|
@@ -69,7 +69,10 @@ class MethodHandles: AllStatic {
|
|
enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
|
|
|
|
// CallSite support
|
|
- static InstanceKlass* get_call_site_context(oop call_site);
|
|
+ static void add_dependent_nmethod(oop call_site, nmethod* nm);
|
|
+ static void remove_dependent_nmethod(oop call_site, nmethod* nm);
|
|
+
|
|
+ static void flush_dependent_nmethods(Handle call_site, Handle target);
|
|
|
|
// Generate MethodHandles adapters.
|
|
static void generate_adapters();
|
|
diff --git a/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
|
|
index 11e46ed03..d65bf4242 100644
|
|
--- a/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
|
|
+++ b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
|
|
@@ -24,11 +24,15 @@
|
|
/**
|
|
* @test
|
|
* @bug 8057967
|
|
- * @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest
|
|
+ * @run main/bootclasspath/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceClassUnloading
|
|
+ * -XX:+PrintCompilation -XX:+TraceDependencies -XX:+TraceReferenceGC
|
|
+ * -verbose:gc java.lang.invoke.CallSiteDepContextTest
|
|
*/
|
|
package java.lang.invoke;
|
|
|
|
import java.lang.ref.*;
|
|
+import java.lang.reflect.Field;
|
|
+
|
|
import jdk.internal.org.objectweb.asm.*;
|
|
import sun.misc.Unsafe;
|
|
|
|
@@ -95,6 +99,13 @@ public class CallSiteDepContextTest {
|
|
}
|
|
}
|
|
|
|
+ public static void testHiddenDepField() throws Exception {
|
|
+ try {
|
|
+ Field f = MethodHandleNatives.CallSiteContext.class.getDeclaredField("vmdependencies");
|
|
+ throw new AssertionError("Context.dependencies field should be hidden");
|
|
+ } catch(NoSuchFieldException e) { /* 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);
|
|
@@ -131,12 +142,14 @@ public class CallSiteDepContextTest {
|
|
static ReferenceQueue rq = new ReferenceQueue();
|
|
static PhantomReference ref;
|
|
|
|
- public static void testGC() throws Throwable {
|
|
+ public static void testGC(boolean clear, boolean precompile) throws Throwable {
|
|
+ String id = "_" + clear + "_" + precompile;
|
|
+
|
|
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),
|
|
+ UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1" + id), null),
|
|
+ UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2" + id), null),
|
|
};
|
|
|
|
MethodHandle[] mhs = new MethodHandle[] {
|
|
@@ -150,30 +163,38 @@ public class CallSiteDepContextTest {
|
|
execute(1, mhs);
|
|
|
|
ref = new PhantomReference<>(cls[0], rq);
|
|
- cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3"), null);
|
|
+ cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3" + id), null);
|
|
mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
|
|
|
|
do {
|
|
System.gc();
|
|
try {
|
|
- Reference ref1 = rq.remove(1000);
|
|
+ Reference ref1 = rq.remove(100);
|
|
if (ref1 == ref) {
|
|
- ref1.clear();
|
|
- System.gc(); // Ensure that the stale context is cleared
|
|
break;
|
|
}
|
|
} catch(InterruptedException e) { /* ignore */ }
|
|
} while (true);
|
|
|
|
- execute(1, mhs);
|
|
+ if (clear) {
|
|
+ ref.clear();
|
|
+ System.gc(); // Ensure that the stale context is unloaded
|
|
+ }
|
|
+ if (precompile) {
|
|
+ execute(1, mhs);
|
|
+ }
|
|
mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
|
|
execute(2, mhs);
|
|
}
|
|
|
|
public static void main(String[] args) throws Throwable {
|
|
+ testHiddenDepField();
|
|
testSharedCallSite();
|
|
testNonBoundCallSite();
|
|
- testGC();
|
|
+ testGC(false, false);
|
|
+ testGC(false, true);
|
|
+ testGC( true, false);
|
|
+ testGC( true, true);
|
|
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 11e452b96..13cf24ca0 100644
|
|
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java
|
|
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java
|
|
@@ -27,8 +27,6 @@ package java.lang.invoke;
|
|
|
|
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},
|
|
@@ -138,47 +136,9 @@ 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.
|
|
+ * JVM uses CallSite.context to store nmethod dependencies on the call site target.
|
|
*/
|
|
- 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).
|
|
- }
|
|
- }
|
|
+ private final MethodHandleNatives.CallSiteContext context = MethodHandleNatives.CallSiteContext.make(this);
|
|
|
|
/**
|
|
* Returns the type of this call site's target.
|
|
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
|
|
index 9a1343c50..899a409e4 100644
|
|
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
|
|
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
|
|
@@ -30,6 +30,7 @@ import java.lang.reflect.Field;
|
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
|
import static java.lang.invoke.MethodHandleStatics.*;
|
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|
+import sun.misc.Cleaner;
|
|
|
|
/**
|
|
* The JVM interface for the method handles package is all here.
|
|
@@ -71,8 +72,27 @@ 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);
|
|
+ /** Represents a context to track nmethod dependencies on CallSite instance target. */
|
|
+ static class CallSiteContext implements Runnable {
|
|
+ //@Injected JVM_nmethodBucket* vmdependencies;
|
|
+
|
|
+ static CallSiteContext make(CallSite cs) {
|
|
+ final CallSiteContext newContext = new CallSiteContext();
|
|
+ // Cleaner is attached to CallSite instance and it clears native structures allocated for CallSite context.
|
|
+ // Though the CallSite can become unreachable, its Context is retained by the Cleaner instance (which is
|
|
+ // referenced from Cleaner class) until cleanup is performed.
|
|
+ Cleaner.create(cs, newContext);
|
|
+ return newContext;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ MethodHandleNatives.clearCallSiteContext(this);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /** Invalidate all recorded nmethods. */
|
|
+ private static native void clearCallSiteContext(CallSiteContext context);
|
|
|
|
private static native void registerNatives();
|
|
static {
|
|
@@ -317,7 +337,6 @@ class MethodHandleNatives {
|
|
return Invokers.linkToTargetMethod(type);
|
|
} else {
|
|
appendixResult[0] = callSite;
|
|
- callSite.initContext(caller);
|
|
return Invokers.linkToCallSiteMethod(type);
|
|
}
|
|
}
|
|
--
|
|
2.12.3
|
|
|