# HG changeset patch # User vlivanov # Date 1431706991 -10800 # Node ID 01a5c5fa56810d31ddffb01f72be5f81aeb2eb29 # Parent e3040e4dde63b8034e9b81a8d0734c64e65a5030 8079205: CallSite dependency tracking is broken after sun.misc.Cleaner became automatically cleared Reviewed-by: roland, psandoz, plevart, kbarrett, jrose diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/ci/ciCallSite.cpp --- a/hotspot/src/share/vm/ci/ciCallSite.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/ci/ciCallSite.cpp Fri May 15 19:23:11 2015 +0300 @@ -50,25 +50,6 @@ } // ------------------------------------------------------------------ -// 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/ci/ciCallSite.hpp --- a/hotspot/src/share/vm/ci/ciCallSite.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/ci/ciCallSite.hpp Fri May 15 19:23:11 2015 +0300 @@ -43,7 +43,6 @@ // Return the target MethodHandle of this CallSite. ciMethodHandle* get_target() const; - ciKlass* get_context(); void print(); }; diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/ci/ciInstanceKlass.cpp --- a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Fri May 15 19:23:11 2015 +0300 @@ -453,8 +453,12 @@ 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/classfile/javaClasses.cpp --- a/hotspot/src/share/vm/classfile/javaClasses.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Fri May 15 19:23:11 2015 +0300 @@ -2967,47 +2967,42 @@ 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() { 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) { - assert(java_lang_invoke_CallSite::is_instance(call_site), ""); - - oop dep_oop = call_site->obj_field_volatile(_context_offset); - return dep_oop; -} - -void java_lang_invoke_CallSite::set_context_volatile(oop call_site, oop context) { +oop java_lang_invoke_CallSite::context(oop call_site) { assert(java_lang_invoke_CallSite::is_instance(call_site), ""); - call_site->obj_field_put_volatile(_context_offset, context); -} - -bool java_lang_invoke_CallSite::set_context_cas(oop call_site, oop context, oop expected) { - assert(java_lang_invoke_CallSite::is_instance(call_site), ""); - HeapWord* context_addr = call_site->obj_field_addr(_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); + + oop dep_oop = call_site->obj_field(_context_offset); + return dep_oop; +} + +// Support for java_lang_invoke_MethodHandleNatives_CallSiteContext + +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 @@ -3403,6 +3398,7 @@ 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 // changed with the new reflection implementation in JDK 1.4, and diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/classfile/javaClasses.hpp --- a/hotspot/src/share/vm/classfile/javaClasses.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Fri May 15 19:23:11 2015 +0300 @@ -1170,8 +1170,6 @@ private: static int _target_offset; static int _context_offset; - static int _default_context_offset; - static void compute_offsets(); @@ -1181,11 +1179,7 @@ static void set_target( oop site, oop target); static void set_target_volatile( oop site, oop 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) { @@ -1197,6 +1191,31 @@ 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 bool is_instance(oop obj); +}; + // Interface to java.security.AccessControlContext objects class java_security_AccessControlContext: AllStatic { @@ -1406,7 +1425,8 @@ #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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/classfile/javaClasses.inline.hpp --- a/hotspot/src/share/vm/classfile/javaClasses.inline.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/classfile/javaClasses.inline.hpp Fri May 15 19:23:11 2015 +0300 @@ -49,6 +49,10 @@ return obj != NULL && is_subclass(obj->klass()); } +inline bool java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); +} + inline bool java_lang_invoke_MemberName::is_instance(oop obj) { return obj != NULL && is_subclass(obj->klass()); } diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/classfile/systemDictionary.hpp --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Fri May 15 19:23:11 2015 +0300 @@ -159,6 +159,7 @@ do_klass(MethodType_klass, java_lang_invoke_MethodType, Pre ) \ do_klass(BootstrapMethodError_klass, java_lang_BootstrapMethodError, Pre ) \ do_klass(CallSite_klass, java_lang_invoke_CallSite, Pre ) \ + do_klass(Context_klass, java_lang_invoke_MethodHandleNatives_CallSiteContext, Pre ) \ do_klass(ConstantCallSite_klass, java_lang_invoke_ConstantCallSite, Pre ) \ do_klass(MutableCallSite_klass, java_lang_invoke_MutableCallSite, Pre ) \ do_klass(VolatileCallSite_klass, java_lang_invoke_VolatileCallSite, Pre ) \ diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/classfile/vmSymbols.hpp --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Fri May 15 19:23:11 2015 +0300 @@ -274,12 +274,14 @@ /* 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;") \ 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;") \ @@ -401,7 +403,7 @@ template(protection_domain_name, "protection_domain") \ 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/code/codeCache.cpp --- a/hotspot/src/share/vm/code/codeCache.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/code/codeCache.cpp Fri May 15 19:23:11 2015 +0300 @@ -1047,40 +1047,6 @@ } } -// Flushes compiled methods dependent on a particular CallSite -// instance when its target is different than the given MethodHandle. -void CodeCache::flush_dependents_on(Handle call_site, Handle method_handle) { - assert_lock_strong(Compile_lock); - - if (number_of_nmethods_with_dependencies() == 0) return; - - // CodeCache can only be updated by a thread_in_VM and they will all be - // stopped during 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 CodeCache::flush_evol_dependents_on(instanceKlassHandle ev_k_h) { diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/code/codeCache.hpp --- a/hotspot/src/share/vm/code/codeCache.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/code/codeCache.hpp Fri May 15 19:23:11 2015 +0300 @@ -224,7 +224,6 @@ // 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/code/dependencies.cpp --- a/hotspot/src/share/vm/code/dependencies.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/code/dependencies.cpp Fri May 15 19:23:11 2015 +0300 @@ -117,9 +117,7 @@ } 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, @@ -175,7 +173,6 @@ } } } 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; @@ -389,7 +386,7 @@ 3, // unique_concrete_subtypes_2 ctxk, k1, k2 3, // unique_concrete_methods_2 ctxk, m1, m2 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) { @@ -1515,16 +1512,11 @@ return find_finalizable_subclass(search_at); } -Klass* Dependencies::check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes) { +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"); - assert(!oopDesc::is_null(method_handle), "sanity"); - Klass* call_site_ctxk = MethodHandles::get_call_site_context(call_site); - assert(!Klass::is_null(call_site_ctxk), "call site context should be initialized already"); - if (recorded_ctxk != call_site_ctxk) { - // Stale context - return recorded_ctxk; - } if (changes == NULL) { // Validate all CallSites if (java_lang_invoke_CallSite::target(call_site) != method_handle) @@ -1599,7 +1591,7 @@ 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/code/dependencies.hpp --- a/hotspot/src/share/vm/code/dependencies.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/code/dependencies.hpp Fri May 15 19:23:11 2015 +0300 @@ -173,7 +173,7 @@ 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), @@ -330,7 +330,7 @@ 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 @@ -496,7 +496,7 @@ 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; } uintptr_t get_identifier(int i); int argument_count() { return dep_args(type()); } diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/code/nmethod.cpp --- a/hotspot/src/share/vm/code/nmethod.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/code/nmethod.cpp Fri May 15 19:23:11 2015 +0300 @@ -565,13 +565,18 @@ // 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")) { @@ -1464,13 +1469,20 @@ 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)) { - InstanceKlass::cast(klass)->remove_dependent_nmethod(this); + 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)) { + InstanceKlass::cast(klass)->remove_dependent_nmethod(this); + } } } } diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Fri May 15 19:23:11 2015 +0300 @@ -1830,11 +1830,10 @@ // 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. @@ -1842,7 +1841,6 @@ 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(); @@ -1850,36 +1848,119 @@ nm->mark_for_deoptimization(); found++; } - b = b->next(); } return found; } +// +// 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. Returns new head of the list. +// +nmethodBucket* nmethodBucket::add_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + for (nmethodBucket* b = deps; b != NULL; b = b->next()) { + if (nm == b->get_nmethod()) { + b->increment(); + return deps; + } + } + return new nmethodBucket(nm, deps); +} + +// +// Decrement count of the nmethod in the dependency list and remove +// 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 dependencies. Returns true if the bucket is ready for reclamation. +// +bool nmethodBucket::remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + + for (nmethodBucket* b = deps; b != NULL; b = b->next()) { + if (nm == b->get_nmethod()) { + int val = b->decrement(); + guarantee(val >= 0, err_msg("Underflow: %d", val)); + return (val == 0); + } + } +#ifdef ASSERT + tty->print_raw_cr("### can't find dependent nmethod"); + nm->print(); +#endif // ASSERT + ShouldNotReachHere(); + return false; +} + +// +// 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 nmethodBucket::print_dependent_nmethods(nmethodBucket* deps, bool verbose) { + int idx = 0; + for (nmethodBucket* b = deps; b != NULL; b = b->next()) { + nmethod* nm = b->get_nmethod(); + tty->print("[%d] count=%d { ", idx++, b->count()); + if (!verbose) { + nm->print_on(tty, "nmethod"); + tty->print_cr(" } "); + } else { + nm->print(); + nm->print_dependencies(); + tty->print_cr("--- } "); + } + } +} + +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(); + assert(count >= 0, err_msg("count shouldn't be negative: %d", count)); +#endif + return true; + } + } + 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()) { - 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; - } + _dependencies = nmethodBucket::clean_dependent_nmethods(_dependencies); set_has_unloaded_dependent(false); } #ifdef ASSERT @@ -1893,90 +1974,26 @@ #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. -// void InstanceKlass::add_dependent_nmethod(nmethod* nm) { assert_locked_or_safepoint(CodeCache_lock); - nmethodBucket* b = _dependencies; - nmethodBucket* last = NULL; - while (b != NULL) { - if (nm == b->get_nmethod()) { - b->increment(); - return; - } - b = b->next(); - } - _dependencies = new nmethodBucket(nm, _dependencies); + _dependencies = nmethodBucket::add_dependent_nmethod(_dependencies, nm); } - -// -// Decrement count of the nmethod in the dependency list and remove -// the bucket competely 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) { assert_locked_or_safepoint(CodeCache_lock); - nmethodBucket* b = _dependencies; - nmethodBucket* last = NULL; - while (b != NULL) { - if (nm == b->get_nmethod()) { - int val = b->decrement(); - guarantee(val >= 0, err_msg("Underflow: %d", val)); - if (val == 0) { - set_has_unloaded_dependent(true); - } - return; - } - last = b; - b = b->next(); + + if (nmethodBucket::remove_dependent_nmethod(_dependencies, nm)) { + set_has_unloaded_dependent(true); } -#ifdef ASSERT - tty->print_cr("### %s can't find dependent nmethod:", this->external_name()); - nm->print(); -#endif // ASSERT - ShouldNotReachHere(); } - #ifndef PRODUCT void InstanceKlass::print_dependent_nmethods(bool verbose) { - nmethodBucket* b = _dependencies; - int idx = 0; - while (b != NULL) { - nmethod* nm = b->get_nmethod(); - tty->print("[%d] count=%d { ", idx++, b->count()); - if (!verbose) { - nm->print_on(tty, "nmethod"); - tty->print_cr(" } "); - } else { - nm->print(); - nm->print_dependencies(); - tty->print_cr("--- } "); - } - b = b->next(); - } + nmethodBucket::print_dependent_nmethods(_dependencies, verbose); } - bool InstanceKlass::is_dependent_nmethod(nmethod* nm) { - nmethodBucket* b = _dependencies; - while (b != NULL) { - if (nm == b->get_nmethod()) { -#ifdef ASSERT - int count = b->count(); - assert(count >= 0, err_msg("count shouldn't be negative: %d", count)); -#endif - return true; - } - b = b->next(); - } - return false; + return nmethodBucket::is_dependent_nmethod(_dependencies, nm); } #endif //PRODUCT diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Fri May 15 19:23:11 2015 +0300 @@ -1297,6 +1297,15 @@ 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); + 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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/prims/methodHandles.cpp --- a/hotspot/src/share/vm/prims/methodHandles.cpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/prims/methodHandles.cpp Fri May 15 19:23:11 2015 +0300 @@ -942,22 +942,56 @@ 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); + } +} + +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); + } } - 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::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); + } + } } - return InstanceKlass::cast(java_lang_Class::as_Klass(context_class_oop)); + if (marked > 0) { + // At least one nmethod has been marked for deoptimization + VM_Deoptimize op; + VMThread::execute(&op); + } } //------------------------------------------------------------------------------ @@ -1276,7 +1310,7 @@ { // Walk all nmethods depending on this call site. MutexLocker mu(Compile_lock, thread); - CodeCache::flush_dependents_on(call_site, target); + MethodHandles::flush_dependent_nmethods(call_site, target); java_lang_invoke_CallSite::set_target(call_site(), target()); } } @@ -1288,30 +1322,34 @@ { // Walk all nmethods depending on this call site. MutexLocker mu(Compile_lock, thread); - CodeCache::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; @@ -1357,6 +1395,7 @@ #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) @@ -1374,7 +1413,7 @@ {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 -r e3040e4dde63 -r 01a5c5fa5681 hotspot/src/share/vm/prims/methodHandles.hpp --- a/hotspot/src/share/vm/prims/methodHandles.hpp Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/src/share/vm/prims/methodHandles.hpp Fri May 15 19:23:11 2015 +0300 @@ -69,7 +69,10 @@ 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 bool generate_adapters(); diff -r e3040e4dde63 -r 01a5c5fa5681 hotspot/test/compiler/jsr292/CallSiteDepContextTest.java --- a/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java Fri May 15 19:21:46 2015 +0300 +++ b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java Fri May 15 19:23:11 2015 +0300 @@ -24,12 +24,15 @@ /** * @test * @bug 8057967 - * @ignore 8079205 - * @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest + * @run main/bootclasspath -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; @@ -96,6 +99,13 @@ } } + 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); @@ -132,12 +142,14 @@ 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[] { @@ -151,30 +163,38 @@ 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"); } }