src/share/vm/oops/instanceKlass.cpp
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File
*** old/src/share/vm/oops/instanceKlass.cpp Thu Nov 5 21:52:43 2015
--- new/src/share/vm/oops/instanceKlass.cpp Thu Nov 5 21:52:43 2015
*** 201,211 ****
--- 201,210 ----
bool is_anonymous) {
No_Safepoint_Verifier no_safepoint; // until k becomes parsable
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);
set_nonstatic_oop_map_size(nonstatic_oop_map_size);
set_access_flags(access_flags);
*** 230,240 ****
--- 229,239 ----
set_array_name(NULL);
set_inner_classes(NULL);
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);
set_oop_map_cache(NULL);
set_jni_ids(NULL);
*** 244,254 ****
--- 243,252 ----
set_generic_signature_index(0);
release_set_methods_jmethod_ids(NULL);
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);
set_minor_version(0);
set_major_version(0);
*** 1862,1875 ****
--- 1860,1872 ----
//
// Walk the list of dependent nmethods searching for nmethods which
// 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.
if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) {
if (TraceDependencies) {
*** 1885,1964 ****
--- 1882,1963 ----
}
return found;
}
//
! // 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 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, optionally, remove
! // the bucket completely when the count goes to 0. This method must find
// 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) {
+ // 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.
//
! 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) {
if (last == NULL) {
first = next;
*** 1970,1986 ****
--- 1969,2005 ----
} else {
last = b;
}
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) {
nm->print_on(tty, "nmethod");
tty->print_cr(" } ");
*** 1990,2064 ****
--- 2009,2074 ----
tty->print_cr("--- } ");
}
}
}
! 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();
assert(count >= 0, "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);
! 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
void InstanceKlass::clean_weak_instanceklass_links(BoolObjectClosure* is_alive) {
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) {
assert(class_loader_data()->is_alive(is_alive), "this klass should be live");
if (is_interface()) {
*** 2101,2110 ****
--- 2111,2122 ----
}
init_implementor();
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();
}
*** 2230,2245 ****
--- 2242,2255 ----
set_member_names(NULL);
}
}
// 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
if (breakpoints() != 0x0) {
methods_do(clear_all_breakpoints);
*** 3578,3772 ****
--- 3588,3672 ----
/////////////// Unit tests ///////////////
#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<nmethod*>(0x8 * 0);
_nmethodMiddle = reinterpret_cast<nmethod*>(0x8 * 1);
_nmethodFirst = reinterpret_cast<nmethod*>(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<nmethod*>(0x8 * 0);
! _nmethods[1] = reinterpret_cast<nmethod*>(0x8 * 1);
! _nmethods[2] = reinterpret_cast<nmethod*>(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 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();
}
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;
+ assert(!has_stale_entries(depContext), "check");
! assert(!depContext.is_dependent_nmethod(nm), "check");
}
- 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");
assert(c._bucketFirst->count() == 0, "check");
assert(c._bucketMiddle->count() == 1, "check");
assert(c._bucketLast->count() == 1, "check");
! testRemoveDependentNmethod(0, false);
+ testRemoveDependentNmethod(1, false);
! testRemoveDependentNmethod(2, false);
+
! testRemoveDependentNmethod(0, true);
! testRemoveDependentNmethod(1, true);
! testRemoveDependentNmethod(2, true);
}
- 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");
! testRemoveDependentNmethod();
}
! 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 bool has_stale_entries(DependencyContext ctx) {
! assert(ctx.has_stale_entries() == ctx.find_stale_entries(), "check");
+ return ctx.has_stale_entries();
}
! static void testRemoveDependentNmethod() {
! testRemoveDependentNmethodFirstDeleteImmediately();
! testRemoveDependentNmethodMiddleDeleteImmediately();
! testRemoveDependentNmethodLastDeleteImmediately();
testRemoveDependentNmethodFirstDeleteDeferred();
! testRemoveDependentNmethodMiddleDeleteDeferred();
! testRemoveDependentNmethodLastDeleteDeferred();
testRemoveDependentNmethodConvenienceFirst();
testRemoveDependentNmethodConvenienceMiddle();
testRemoveDependentNmethodConvenienceLast();
! 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;
}
static void test() {
testRemoveDependentNmethod();
}
};
! void TestNmethodBucket_test() {
! TestNmethodBucket::test();
! void TestDependencyContext_test() {
! TestDependencyContext::test();
}
#endif
src/share/vm/oops/instanceKlass.cpp
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File