--- old/src/share/vm/oops/instanceKlass.cpp 2015-11-05 21:52:43.000000000 +0300 +++ new/src/share/vm/oops/instanceKlass.cpp 2015-11-05 21:52:43.000000000 +0300 @@ -203,7 +203,6 @@ int iksize = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, access_flags.is_interface(), is_anonymous); - set_vtable_length(vtable_len); set_itable_length(itable_len); set_static_field_size(static_field_size); @@ -232,7 +231,7 @@ set_static_oop_field_count(0); set_nonstatic_field_size(0); set_is_marked_dependent(false); - set_has_unloaded_dependent(false); + _dep_context = DependencyContext::EMPTY; set_init_state(InstanceKlass::allocated); set_init_thread(NULL); set_reference_type(rt); @@ -246,7 +245,6 @@ set_annotations(NULL); set_jvmti_cached_class_field_map(NULL); set_initial_method_idnum(0); - _dependencies = NULL; set_jvmti_cached_class_field_map(NULL); set_cached_class_file(NULL); set_initial_method_idnum(0); @@ -1864,10 +1862,9 @@ // are dependent on the changes that were passed in and mark them for // deoptimization. Returns the number of nmethods found. // -int nmethodBucket::mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes) { - assert_locked_or_safepoint(CodeCache_lock); +int DependencyContext::mark_dependent_nmethods(DepChange& changes) { int found = 0; - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { + for (nmethodBucket* b = dependencies(); 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. @@ -1887,76 +1884,78 @@ } // -// Add an nmethodBucket to the list of dependencies for this nmethod. +// Add an nmethod to the dependency context. // 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()) { +void DependencyContext::add_dependent_nmethod(nmethod* nm, bool expunge) { + assert_lock_strong(CodeCache_lock); + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { if (nm == b->get_nmethod()) { b->increment(); - return deps; + return; } } - return new nmethodBucket(nm, deps); + set_dependencies(new nmethodBucket(nm, dependencies())); + + if (expunge) { + // Remove stale entries from the list. + expunge_stale_entries(); + } } // -// 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 was deleted, -// or marked ready for reclaimation. -bool nmethodBucket::remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately) { +// Decrement count of the nmethod in the dependency list and, optionally, 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. +void DependencyContext::remove_dependent_nmethod(nmethod* nm, bool expunge) { assert_locked_or_safepoint(CodeCache_lock); - - nmethodBucket* first = *deps; + nmethodBucket* first = dependencies(); nmethodBucket* last = NULL; - for (nmethodBucket* b = first; b != NULL; b = b->next()) { if (nm == b->get_nmethod()) { int val = b->decrement(); guarantee(val >= 0, "Underflow: %d", val); if (val == 0) { - if (delete_immediately) { + if (expunge) { if (last == NULL) { - *deps = b->next(); + set_dependencies(b->next()); } else { last->set_next(b->next()); } delete b; + } else { + set_has_stale_entries(true); } } - return true; + if (expunge) { + // Remove stale entries from the list. + expunge_stale_entries(); + } + return; } last = b; } - #ifdef ASSERT 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. +// Reclaim all unused buckets. // -nmethodBucket* nmethodBucket::clean_dependent_nmethods(nmethodBucket* deps) { - nmethodBucket* first = deps; +void DependencyContext::expunge_stale_entries() { + assert_locked_or_safepoint(CodeCache_lock); + if (!has_stale_entries()) { + assert(!find_stale_entries(), "inconsistent info"); + return; + } + nmethodBucket* first = dependencies(); nmethodBucket* last = NULL; - nmethodBucket* b = first; - - while (b != NULL) { + for (nmethodBucket* b = first; b != NULL;) { assert(b->count() >= 0, "bucket count: %d", b->count()); nmethodBucket* next = b->next(); if (b->count() == 0) { @@ -1972,13 +1971,33 @@ } b = next; } - return first; + set_dependencies(first); + set_has_stale_entries(false); +} + +int DependencyContext::remove_all_dependents() { + //assert_lock_strong(CodeCache_lock); // called from InstanceKlass::release_C_heap_structures() + nmethodBucket* b = dependencies(); + set_dependencies(NULL); + int marked = 0; + 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; + } + set_has_stale_entries(false); + return marked; } #ifndef PRODUCT -void nmethodBucket::print_dependent_nmethods(nmethodBucket* deps, bool verbose) { +void DependencyContext::print_dependent_nmethods(bool verbose) { int idx = 0; - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { nmethod* nm = b->get_nmethod(); tty->print("[%d] count=%d { ", idx++, b->count()); if (!verbose) { @@ -1992,8 +2011,8 @@ } } -bool nmethodBucket::is_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { +bool DependencyContext::is_dependent_nmethod(nmethod* nm) { + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { if (nm == b->get_nmethod()) { #ifdef ASSERT int count = b->count(); @@ -2004,51 +2023,40 @@ } return false; } -#endif //PRODUCT -int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { - assert_locked_or_safepoint(CodeCache_lock); - return nmethodBucket::mark_dependent_nmethods(_dependencies, changes); +bool DependencyContext::find_stale_entries() { + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + if (b->count() == 0) return true; + } + return false; } -void InstanceKlass::clean_dependent_nmethods() { - assert_locked_or_safepoint(CodeCache_lock); +#endif //PRODUCT - 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, "bucket count: %d", b->count()); - assert(b->count() != 0, "empty buckets need to be cleaned"); - } - } -#endif +int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { + DependencyContext dep_context(&_dep_context); + return dep_context.mark_dependent_nmethods(changes); } void InstanceKlass::add_dependent_nmethod(nmethod* nm) { - assert_locked_or_safepoint(CodeCache_lock); - _dependencies = nmethodBucket::add_dependent_nmethod(_dependencies, nm); + DependencyContext dep_context(&_dep_context); + dep_context.add_dependent_nmethod(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); - } + DependencyContext dep_context(&_dep_context); + dep_context.remove_dependent_nmethod(nm, delete_immediately); } #ifndef PRODUCT void InstanceKlass::print_dependent_nmethods(bool verbose) { - nmethodBucket::print_dependent_nmethods(_dependencies, verbose); + DependencyContext dep_context(&_dep_context); + dep_context.print_dependent_nmethods(verbose); } bool InstanceKlass::is_dependent_nmethod(nmethod* nm) { - return nmethodBucket::is_dependent_nmethod(_dependencies, nm); + DependencyContext dep_context(&_dep_context); + return dep_context.is_dependent_nmethod(nm); } #endif //PRODUCT @@ -2056,7 +2064,9 @@ clean_implementors_list(is_alive); clean_method_data(is_alive); - clean_dependent_nmethods(); + // Since GC iterates InstanceKlasses sequentially, it is safe to remove stale entries here. + DependencyContext dep_context(&_dep_context); + dep_context.expunge_stale_entries(); } void InstanceKlass::clean_implementors_list(BoolObjectClosure* is_alive) { @@ -2103,6 +2113,8 @@ constants()->remove_unshareable_info(); + assert(_dep_context == DependencyContext::EMPTY, "dependency context is not shareable"); + for (int i = 0; i < methods()->length(); i++) { Method* m = methods()->at(i); m->remove_unshareable_info(); @@ -2232,12 +2244,10 @@ } // release dependencies - nmethodBucket* b = _dependencies; - _dependencies = NULL; - while (b != NULL) { - nmethodBucket* next = b->next(); - delete b; - b = next; + { + DependencyContext ctx(&_dep_context); + int marked = ctx.remove_all_dependents(); + assert(marked == 0, "all dependencies should be already invalidated"); } // Deallocate breakpoint records @@ -3580,193 +3590,83 @@ #ifndef PRODUCT -class TestNmethodBucketContext { +class TestDependencyContext { public: - nmethod* _nmethodLast; - nmethod* _nmethodMiddle; - nmethod* _nmethodFirst; - - nmethodBucket* _bucketLast; - nmethodBucket* _bucketMiddle; - nmethodBucket* _bucketFirst; + nmethod* _nmethods[3]; - nmethodBucket* _bucketList; + intptr_t _dependency_context; - TestNmethodBucketContext() { + TestDependencyContext() : _dependency_context(DependencyContext::EMPTY) { CodeCache_lock->lock_without_safepoint_check(); - _nmethodLast = reinterpret_cast(0x8 * 0); - _nmethodMiddle = reinterpret_cast(0x8 * 1); - _nmethodFirst = reinterpret_cast(0x8 * 2); + DependencyContext depContext(&_dependency_context); - _bucketLast = new nmethodBucket(_nmethodLast, NULL); - _bucketMiddle = new nmethodBucket(_nmethodMiddle, _bucketLast); - _bucketFirst = new nmethodBucket(_nmethodFirst, _bucketMiddle); + _nmethods[0] = reinterpret_cast(0x8 * 0); + _nmethods[1] = reinterpret_cast(0x8 * 1); + _nmethods[2] = reinterpret_cast(0x8 * 2); - _bucketList = _bucketFirst; + depContext.add_dependent_nmethod(_nmethods[2]); + depContext.add_dependent_nmethod(_nmethods[1]); + depContext.add_dependent_nmethod(_nmethods[0]); } - ~TestNmethodBucketContext() { - delete _bucketLast; - delete _bucketMiddle; - delete _bucketFirst; - + ~TestDependencyContext() { + wipe(); CodeCache_lock->unlock(); } -}; - -class TestNmethodBucket { - public: - static void testRemoveDependentNmethodFirstDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodFirst, true /* delete */); - - assert(c._bucketList == c._bucketMiddle, "check"); - assert(c._bucketList->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketFirst = NULL; - } - - static void testRemoveDependentNmethodMiddleDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodMiddle, true /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketMiddle = NULL; - } - - static void testRemoveDependentNmethodLastDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodLast, true /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketLast = NULL; - } - - static void testRemoveDependentNmethodFirstDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodFirst, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 0, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodMiddleDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodMiddle, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 0, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodLastDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodLast, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 0, "check"); - } - - static void testRemoveDependentNmethodConvenienceFirst() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodFirst); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); + static void testRemoveDependentNmethod(int id, bool delete_immediately) { + TestDependencyContext c; + DependencyContext depContext(&c._dependency_context); + assert(!has_stale_entries(depContext), "check"); + + nmethod* nm = c._nmethods[id]; + depContext.remove_dependent_nmethod(nm, delete_immediately); + + if (!delete_immediately) { + assert(has_stale_entries(depContext), "check"); + assert(depContext.is_dependent_nmethod(nm), "check"); + depContext.expunge_stale_entries(); + } - assert(c._bucketFirst->count() == 0, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 1, "check"); + assert(!has_stale_entries(depContext), "check"); + assert(!depContext.is_dependent_nmethod(nm), "check"); } - static void testRemoveDependentNmethodConvenienceMiddle() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodMiddle); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 0, "check"); - assert(c._bucketLast->count() == 1, "check"); + static void testRemoveDependentNmethod() { + testRemoveDependentNmethod(0, false); + testRemoveDependentNmethod(1, false); + testRemoveDependentNmethod(2, false); + + testRemoveDependentNmethod(0, true); + testRemoveDependentNmethod(1, true); + testRemoveDependentNmethod(2, true); } - static void testRemoveDependentNmethodConvenienceLast() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodLast); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 0, "check"); + static void test() { + testRemoveDependentNmethod(); } - static void testRemoveDependentNmethod() { - testRemoveDependentNmethodFirstDeleteImmediately(); - testRemoveDependentNmethodMiddleDeleteImmediately(); - testRemoveDependentNmethodLastDeleteImmediately(); - - testRemoveDependentNmethodFirstDeleteDeferred(); - testRemoveDependentNmethodMiddleDeleteDeferred(); - testRemoveDependentNmethodLastDeleteDeferred(); - - testRemoveDependentNmethodConvenienceFirst(); - testRemoveDependentNmethodConvenienceMiddle(); - testRemoveDependentNmethodConvenienceLast(); + static bool has_stale_entries(DependencyContext ctx) { + assert(ctx.has_stale_entries() == ctx.find_stale_entries(), "check"); + return ctx.has_stale_entries(); } - static void test() { - testRemoveDependentNmethod(); + void wipe() { + DependencyContext ctx(&_dependency_context); + nmethodBucket* b = ctx.dependencies(); + ctx.set_dependencies(NULL); + ctx.set_has_stale_entries(false); + while (b != NULL) { + nmethodBucket* next = b->next(); + delete b; + b = next; + } } }; -void TestNmethodBucket_test() { - TestNmethodBucket::test(); +void TestDependencyContext_test() { + TestDependencyContext::test(); } #endif