8143408: Crash during InstanceKlass unloading when clearing dependency context
Reviewed-by: kvn
--- a/hotspot/src/share/vm/code/dependencyContext.cpp Mon Nov 23 11:06:14 2015 +0100
+++ b/hotspot/src/share/vm/code/dependencyContext.cpp Wed Nov 25 01:17:28 2015 +0300
@@ -218,6 +218,18 @@
return marked;
}
+void DependencyContext::wipe() {
+ assert_locked_or_safepoint(CodeCache_lock);
+ nmethodBucket* b = dependencies();
+ set_dependencies(NULL);
+ set_has_stale_entries(false);
+ while (b != NULL) {
+ nmethodBucket* next = b->next();
+ delete b;
+ b = next;
+ }
+}
+
#ifndef PRODUCT
void DependencyContext::print_dependent_nmethods(bool verbose) {
int idx = 0;
@@ -271,28 +283,31 @@
intptr_t _dependency_context;
+ DependencyContext dependencies() {
+ DependencyContext depContext(&_dependency_context);
+ return depContext;
+ }
+
TestDependencyContext() : _dependency_context(DependencyContext::EMPTY) {
CodeCache_lock->lock_without_safepoint_check();
- DependencyContext depContext(&_dependency_context);
-
_nmethods[0] = reinterpret_cast<nmethod*>(0x8 * 0);
_nmethods[1] = reinterpret_cast<nmethod*>(0x8 * 1);
_nmethods[2] = reinterpret_cast<nmethod*>(0x8 * 2);
- depContext.add_dependent_nmethod(_nmethods[2]);
- depContext.add_dependent_nmethod(_nmethods[1]);
- depContext.add_dependent_nmethod(_nmethods[0]);
+ dependencies().add_dependent_nmethod(_nmethods[2]);
+ dependencies().add_dependent_nmethod(_nmethods[1]);
+ dependencies().add_dependent_nmethod(_nmethods[0]);
}
~TestDependencyContext() {
- wipe();
+ dependencies().wipe();
CodeCache_lock->unlock();
}
static void testRemoveDependentNmethod(int id, bool delete_immediately) {
TestDependencyContext c;
- DependencyContext depContext(&c._dependency_context);
+ DependencyContext depContext = c.dependencies();
assert(!has_stale_entries(depContext), "check");
nmethod* nm = c._nmethods[id];
@@ -326,18 +341,6 @@
assert(ctx.has_stale_entries() == ctx.find_stale_entries(), "check");
return ctx.has_stale_entries();
}
-
- 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 TestDependencyContext_test() {
--- a/hotspot/src/share/vm/code/dependencyContext.hpp Mon Nov 23 11:06:14 2015 +0100
+++ b/hotspot/src/share/vm/code/dependencyContext.hpp Wed Nov 25 01:17:28 2015 +0300
@@ -143,6 +143,10 @@
void expunge_stale_entries();
+ // Unsafe deallocation of nmethodBuckets. Used in IK::release_C_heap_structures
+ // to clean up the context possibly containing live entries pointing to unloaded nmethods.
+ void wipe();
+
#ifndef PRODUCT
void print_dependent_nmethods(bool verbose);
bool is_dependent_nmethod(nmethod* nm);
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Nov 23 11:06:14 2015 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Nov 25 01:17:28 2015 +0300
@@ -2063,12 +2063,16 @@
}
}
- // release dependencies
- {
- DependencyContext ctx(&_dep_context);
- int marked = ctx.remove_all_dependents();
- assert(marked == 0, "all dependencies should be already invalidated");
- }
+ // Release dependencies.
+ // It is desirable to use DC::remove_all_dependents() here, but, unfortunately,
+ // it is not safe (see JDK-8143408). The problem is that the klass dependency
+ // context can contain live dependencies, since there's a race between nmethod &
+ // klass unloading. If the klass is dead when nmethod unloading happens, relevant
+ // dependencies aren't removed from the context associated with the class (see
+ // nmethod::flush_dependencies). It ends up during klass unloading as seemingly
+ // live dependencies pointing to unloaded nmethods and causes a crash in
+ // DC::remove_all_dependents() when it touches unloaded nmethod.
+ dependencies().wipe();
// Deallocate breakpoint records
if (breakpoints() != 0x0) {