Projects
openEuler:24.03:SP1:Everything
openjdk-1.8.0
_service:tar_scm:8079205-CallSite-dependency-tr...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:8079205-CallSite-dependency-tracking-is-broken-after.patch of Package openjdk-1.8.0
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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2