--- old/src/share/vm/ci/ciCallSite.cpp 2015-05-14 15:17:29.000000000 +0300 +++ new/src/share/vm/ci/ciCallSite.cpp 2015-05-14 15:17:29.000000000 +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. --- old/src/share/vm/ci/ciCallSite.hpp 2015-05-14 15:17:29.000000000 +0300 +++ new/src/share/vm/ci/ciCallSite.hpp 2015-05-14 15:17:29.000000000 +0300 @@ -43,7 +43,6 @@ // Return the target MethodHandle of this CallSite. ciMethodHandle* get_target() const; - ciKlass* get_context(); void print(); }; --- old/src/share/vm/ci/ciInstanceKlass.cpp 2015-05-14 15:17:29.000000000 +0300 +++ new/src/share/vm/ci/ciInstanceKlass.cpp 2015-05-14 15:17:29.000000000 +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(); --- old/src/share/vm/classfile/javaClasses.cpp 2015-05-14 15:17:30.000000000 +0300 +++ new/src/share/vm/classfile/javaClasses.cpp 2015-05-14 15:17:30.000000000 +0300 @@ -2967,47 +2967,41 @@ 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_CallSite_Context_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_CallSite_Context + +int java_lang_invoke_CallSite_Context::_dependencies_offset; + +void java_lang_invoke_CallSite_Context::compute_offsets() { + Klass* k = SystemDictionary::Context_klass(); + if (k != NULL) { + compute_offset(_dependencies_offset, k, vmSymbols::dependencies_name(), vmSymbols::long_signature()); + } } -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); - } - return success; +nmethodBucket* java_lang_invoke_CallSite_Context::dependencies(oop call_site) { + assert(java_lang_invoke_CallSite_Context::is_instance(call_site), ""); + return (nmethodBucket*) (address) call_site->long_field(_dependencies_offset); } -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; +void java_lang_invoke_CallSite_Context::set_dependencies(oop call_site, nmethodBucket* context) { + assert(java_lang_invoke_CallSite_Context::is_instance(call_site), ""); + call_site->long_field_put(_dependencies_offset, (jlong) (address) context); } // Support for java_security_AccessControlContext @@ -3403,6 +3397,7 @@ java_lang_invoke_LambdaForm::compute_offsets(); java_lang_invoke_MethodType::compute_offsets(); java_lang_invoke_CallSite::compute_offsets(); + java_lang_invoke_CallSite_Context::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 --- old/src/share/vm/classfile/javaClasses.hpp 2015-05-14 15:17:30.000000000 +0300 +++ new/src/share/vm/classfile/javaClasses.hpp 2015-05-14 15:17:30.000000000 +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,28 @@ static int target_offset_in_bytes() { return _target_offset; } }; +// Interface to java.lang.invoke.CallSite$Context objects + +class java_lang_invoke_CallSite_Context : AllStatic { + friend class JavaClasses; + +private: + static int _dependencies_offset; + + static void compute_offsets(); + +public: + // Accessors + static nmethodBucket* dependencies(oop context); + static void set_dependencies(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 { --- old/src/share/vm/classfile/javaClasses.inline.hpp 2015-05-14 15:17:31.000000000 +0300 +++ new/src/share/vm/classfile/javaClasses.inline.hpp 2015-05-14 15:17:30.000000000 +0300 @@ -49,6 +49,10 @@ return obj != NULL && is_subclass(obj->klass()); } +inline bool java_lang_invoke_CallSite_Context::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()); } --- old/src/share/vm/classfile/systemDictionary.hpp 2015-05-14 15:17:31.000000000 +0300 +++ new/src/share/vm/classfile/systemDictionary.hpp 2015-05-14 15:17:31.000000000 +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_CallSite_Context, 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 ) \ --- old/src/share/vm/classfile/vmSymbols.hpp 2015-05-14 15:17:31.000000000 +0300 +++ new/src/share/vm/classfile/vmSymbols.hpp 2015-05-14 15:17:31.000000000 +0300 @@ -265,12 +265,14 @@ template(java_lang_invoke_DirectMethodHandle, "java/lang/invoke/DirectMethodHandle") \ template(java_lang_invoke_MutableCallSite, "java/lang/invoke/MutableCallSite") \ template(java_lang_invoke_VolatileCallSite, "java/lang/invoke/VolatileCallSite") \ + template(java_lang_invoke_CallSite_Context, "java/lang/invoke/CallSite$Context") \ template(java_lang_invoke_MethodHandle, "java/lang/invoke/MethodHandle") \ template(java_lang_invoke_MethodType, "java/lang/invoke/MethodType") \ template(java_lang_invoke_MethodType_signature, "Ljava/lang/invoke/MethodType;") \ template(java_lang_invoke_MemberName_signature, "Ljava/lang/invoke/MemberName;") \ template(java_lang_invoke_LambdaForm_signature, "Ljava/lang/invoke/LambdaForm;") \ template(java_lang_invoke_MethodHandle_signature, "Ljava/lang/invoke/MethodHandle;") \ + template(java_lang_invoke_CallSite_Context_signature, "Ljava/lang/invoke/CallSite$Context;") \ /* 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") \ --- old/src/share/vm/code/codeCache.cpp 2015-05-14 15:17:31.000000000 +0300 +++ new/src/share/vm/code/codeCache.cpp 2015-05-14 15:17:31.000000000 +0300 @@ -1046,40 +1046,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) { --- old/src/share/vm/code/codeCache.hpp 2015-05-14 15:17:32.000000000 +0300 +++ new/src/share/vm/code/codeCache.hpp 2015-05-14 15:17:32.000000000 +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); --- old/src/share/vm/code/dependencies.cpp 2015-05-14 15:17:32.000000000 +0300 +++ new/src/share/vm/code/dependencies.cpp 2015-05-14 15:17:32.000000000 +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(call_site->is_a(SystemDictionary::CallSite_klass()), "sanity"); + assert(!oopDesc::is_null(call_site), "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; --- old/src/share/vm/code/dependencies.hpp 2015-05-14 15:17:33.000000000 +0300 +++ new/src/share/vm/code/dependencies.hpp 2015-05-14 15:17:33.000000000 +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()); } --- old/src/share/vm/code/nmethod.cpp 2015-05-14 15:17:33.000000000 +0300 +++ new/src/share/vm/code/nmethod.cpp 2015-05-14 15:17:33.000000000 +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) { + 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); + } else { + // CallSite dependencies are managed on per-CallSite instance basis. + oop call_site = deps.argument_oop(0); + MethodHandles::add_dependent_nmethod(call_site, 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) { + 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); + } + } else { + // CallSite dependencies are managed on per-CallSite instance basis. + oop call_site = deps.argument_oop(0); + MethodHandles::remove_dependent_nmethod(call_site, this); } } } --- old/src/share/vm/oops/instanceKlass.cpp 2015-05-14 15:17:33.000000000 +0300 +++ new/src/share/vm/oops/instanceKlass.cpp 2015-05-14 15:17:33.000000000 +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,105 +1848,82 @@ 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. +// recording of dependencies. Returns true if the bucket is ready for reclamation. // -void InstanceKlass::remove_dependent_nmethod(nmethod* nm) { +bool nmethodBucket::remove_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()) { int val = b->decrement(); guarantee(val >= 0, err_msg("Underflow: %d", val)); - if (val == 0) { - set_has_unloaded_dependent(true); - } - return; + return (val == 0); } - 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; } +// +// 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) { @@ -1959,14 +1934,11 @@ 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(); @@ -1974,12 +1946,57 @@ #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) { + assert_locked_or_safepoint(CodeCache_lock); + + if (nmethodBucket::remove_dependent_nmethod(_dependencies, nm)) { + 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 + void InstanceKlass::clean_implementors_list(BoolObjectClosure* is_alive) { assert(class_loader_data()->is_alive(is_alive), "this klass should be live"); if (is_interface()) { --- old/src/share/vm/oops/instanceKlass.hpp 2015-05-14 15:17:34.000000000 +0300 +++ new/src/share/vm/oops/instanceKlass.hpp 2015-05-14 15:17:34.000000000 +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 --- old/src/share/vm/prims/methodHandles.cpp 2015-05-14 15:17:34.000000000 +0300 +++ new/src/share/vm/prims/methodHandles.cpp 2015-05-14 15:17:34.000000000 +0300 @@ -940,22 +940,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. - } - 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::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_CallSite_Context::dependencies(context); + + nmethodBucket* new_deps = nmethodBucket::add_dependent_nmethod(deps, nm); + if (deps != new_deps) { + java_lang_invoke_CallSite_Context::set_dependencies(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_CallSite_Context::dependencies(context); + + if (nmethodBucket::remove_dependent_nmethod(deps, nm)) { + nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps); + if (deps != new_deps) { + java_lang_invoke_CallSite_Context::set_dependencies(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_CallSite_Context::dependencies(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_CallSite_Context::set_dependencies(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)); } //------------------------------------------------------------------------------ @@ -1274,7 +1308,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()); } } @@ -1286,30 +1320,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_CallSite_Context::dependencies(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_CallSite_Context::set_dependencies(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; @@ -1352,6 +1390,7 @@ #define CLS LANG"Class;" #define STRG LANG"String;" #define CS JLINV"CallSite;" +#define CTX JLINV"CallSite$Context;" #define MT JLINV"MethodType;" #define MH JLINV"MethodHandle;" #define MEM JLINV"MemberName;" @@ -1372,7 +1411,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)} --- old/src/share/vm/prims/methodHandles.hpp 2015-05-14 15:17:35.000000000 +0300 +++ new/src/share/vm/prims/methodHandles.hpp 2015-05-14 15:17:35.000000000 +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(); --- old/test/compiler/jsr292/CallSiteDepContextTest.java 2015-05-14 15:17:35.000000000 +0300 +++ new/test/compiler/jsr292/CallSiteDepContextTest.java 2015-05-14 15:17:35.000000000 +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,14 @@ } } + public static void testHiddenDepField() throws Exception { + Class contextClass = Class.forName("java.lang.invoke.CallSite$Context"); + try { + Field f = contextClass.getDeclaredField("dependencies"); + 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 +143,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 +164,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"); } }