< prev index next >
src/hotspot/share/code/compiledMethod.cpp
#include "precompiled.hpp"
#include "code/compiledIC.hpp"
#include "code/compiledMethod.inline.hpp"
#include "code/scopeDesc.hpp"
#include "code/codeCache.hpp"
+ #include "code/icBuffer.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/gcBehaviours.hpp"
#include "interpreter/bytecode.inline.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
***************
assert(klass->is_loader_alive(), "must be alive");
}
#endif // ASSERT
! void CompiledMethod::clean_ic_if_metadata_is_dead(CompiledIC *ic) {
if (ic->is_icholder_call()) {
// The only exception is compiledICHolder metdata which may
// yet be marked below. (We check this further below).
CompiledICHolder* cichk_metdata = ic->cached_icholder();
if (cichk_metdata->is_loader_alive()) {
! return;
}
} else {
Metadata* ic_metdata = ic->cached_metadata();
if (ic_metdata != NULL) {
if (ic_metdata->is_klass()) {
if (((Klass*)ic_metdata)->is_loader_alive()) {
! return;
}
} else if (ic_metdata->is_method()) {
Method* method = (Method*)ic_metdata;
assert(!method->is_old(), "old method should have been cleaned");
if (method->method_holder()->is_loader_alive()) {
! return;
}
} else {
ShouldNotReachHere();
}
}
}
! ic->set_to_clean();
}
// static_stub_Relocations may have dangling references to
// nmethods so trim them out here. Otherwise it looks like
// compiled code is maintaining a link to dead metadata.
assert(klass->is_loader_alive(), "must be alive");
}
#endif // ASSERT
! bool CompiledMethod::clean_ic_if_metadata_is_dead(CompiledIC *ic) {
if (ic->is_icholder_call()) {
// The only exception is compiledICHolder metdata which may
// yet be marked below. (We check this further below).
CompiledICHolder* cichk_metdata = ic->cached_icholder();
if (cichk_metdata->is_loader_alive()) {
! return true;
}
} else {
Metadata* ic_metdata = ic->cached_metadata();
if (ic_metdata != NULL) {
if (ic_metdata->is_klass()) {
if (((Klass*)ic_metdata)->is_loader_alive()) {
! return true;
}
} else if (ic_metdata->is_method()) {
Method* method = (Method*)ic_metdata;
assert(!method->is_old(), "old method should have been cleaned");
if (method->method_holder()->is_loader_alive()) {
! return true;
}
} else {
ShouldNotReachHere();
}
}
}
! if (ic->is_clean()) {
! return true;
! }
! return ic->set_to_clean();
}
// static_stub_Relocations may have dangling references to
// nmethods so trim them out here. Otherwise it looks like
// compiled code is maintaining a link to dead metadata.
***************
#endif
}
// Clean references to unloaded nmethods at addr from this one, which is not unloaded.
template <class CompiledICorStaticCall>
! static void clean_if_nmethod_is_unloaded(CompiledICorStaticCall *ic, address addr, CompiledMethod* from,
bool clean_all) {
// Ok, to lookup references to zombies here
CodeBlob *cb = CodeCache::find_blob_unsafe(addr);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL) {
// Clean inline caches pointing to both zombie and not_entrant methods
if (clean_all || !nm->is_in_use() || nm->is_unloading() || (nm->method()->code() != nm)) {
! ic->set_to_clean(from->is_alive());
assert(ic->is_clean(), "nmethod " PTR_FORMAT "not clean %s", p2i(from), from->method()->name_and_sig_as_C_string());
}
}
}
! static void clean_if_nmethod_is_unloaded(CompiledIC *ic, CompiledMethod* from,
bool clean_all) {
! clean_if_nmethod_is_unloaded(ic, ic->ic_destination(), from, clean_all);
}
! static void clean_if_nmethod_is_unloaded(CompiledStaticCall *csc, CompiledMethod* from,
bool clean_all) {
! clean_if_nmethod_is_unloaded(csc, csc->destination(), from, clean_all);
}
// Cleans caches in nmethods that point to either classes that are unloaded
// or nmethods that are unloaded.
//
// Can be called either in parallel by G1 currently or after all
// nmethods are unloaded. Return postponed=true in the parallel case for
// inline caches found that point to nmethods that are not yet visited during
// the do_unloading walk.
! void CompiledMethod::unload_nmethod_caches(bool unloading_occurred) {
ResourceMark rm;
// Exception cache only needs to be called if unloading occurred
if (unloading_occurred) {
clean_exception_cache();
}
! cleanup_inline_caches_impl(unloading_occurred, false);
// All static stubs need to be cleaned.
clean_ic_stubs();
// Check that the metadata embedded in the nmethod is alive
DEBUG_ONLY(metadata_do(check_class));
}
// Called to clean up after class unloading for live nmethods and from the sweeper
// for all methods.
! void CompiledMethod::cleanup_inline_caches_impl(bool unloading_occurred, bool clean_all) {
assert(CompiledICLocker::is_safe(this), "mt unsafe call");
ResourceMark rm;
// Find all calls in an nmethod and clear the ones that point to non-entrant,
// zombie and unloaded nmethods.
#endif
}
// Clean references to unloaded nmethods at addr from this one, which is not unloaded.
template <class CompiledICorStaticCall>
! static bool clean_if_nmethod_is_unloaded(CompiledICorStaticCall *ic, address addr, CompiledMethod* from,
bool clean_all) {
// Ok, to lookup references to zombies here
CodeBlob *cb = CodeCache::find_blob_unsafe(addr);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL) {
// Clean inline caches pointing to both zombie and not_entrant methods
if (clean_all || !nm->is_in_use() || nm->is_unloading() || (nm->method()->code() != nm)) {
! if (!ic->set_to_clean(from->is_alive())) {
! return false;
! }
assert(ic->is_clean(), "nmethod " PTR_FORMAT "not clean %s", p2i(from), from->method()->name_and_sig_as_C_string());
}
}
+ return true;
}
! static bool clean_if_nmethod_is_unloaded(CompiledIC *ic, CompiledMethod* from,
bool clean_all) {
! return clean_if_nmethod_is_unloaded(ic, ic->ic_destination(), from, clean_all);
}
! static bool clean_if_nmethod_is_unloaded(CompiledStaticCall *csc, CompiledMethod* from,
bool clean_all) {
! return clean_if_nmethod_is_unloaded(csc, csc->destination(), from, clean_all);
}
// Cleans caches in nmethods that point to either classes that are unloaded
// or nmethods that are unloaded.
//
// Can be called either in parallel by G1 currently or after all
// nmethods are unloaded. Return postponed=true in the parallel case for
// inline caches found that point to nmethods that are not yet visited during
// the do_unloading walk.
! bool CompiledMethod::unload_nmethod_caches(bool unloading_occurred) {
ResourceMark rm;
// Exception cache only needs to be called if unloading occurred
if (unloading_occurred) {
clean_exception_cache();
}
! if (!cleanup_inline_caches_impl(unloading_occurred, false)) {
! return false;
! }
// All static stubs need to be cleaned.
clean_ic_stubs();
// Check that the metadata embedded in the nmethod is alive
DEBUG_ONLY(metadata_do(check_class));
+ return true;
+ }
+
+ void CompiledMethod::cleanup_inline_caches(bool clean_all) {
+ for (;;) {
+ { CompiledICLocker ic_locker(this);
+ if (cleanup_inline_caches_impl(false, clean_all)) {
+ return;
+ }
+ }
+ InlineCacheBuffer::refill_ic_stubs();
+ }
}
// Called to clean up after class unloading for live nmethods and from the sweeper
// for all methods.
! bool CompiledMethod::cleanup_inline_caches_impl(bool unloading_occurred, bool clean_all) {
assert(CompiledICLocker::is_safe(this), "mt unsafe call");
ResourceMark rm;
// Find all calls in an nmethod and clear the ones that point to non-entrant,
// zombie and unloaded nmethods.
***************
case relocInfo::virtual_call_type:
if (unloading_occurred) {
// If class unloading occurred we first clear ICs where the cached metadata
// is referring to an unloaded klass or method.
! clean_ic_if_metadata_is_dead(CompiledIC_at(&iter));
}
! clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all);
break;
case relocInfo::opt_virtual_call_type:
! clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all);
break;
case relocInfo::static_call_type:
! clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), this, clean_all);
! break;
!
! case relocInfo::oop_type:
break;
- case relocInfo::metadata_type:
- break; // nothing to do.
-
default:
break;
}
}
}
// Iterating over all nmethods, e.g. with the help of CodeCache::nmethods_do(fun) was found
// to not be inherently safe. There is a chance that fields are seen which are not properly
// initialized. This happens despite the fact that nmethods_do() asserts the CodeCache_lock
case relocInfo::virtual_call_type:
if (unloading_occurred) {
// If class unloading occurred we first clear ICs where the cached metadata
// is referring to an unloaded klass or method.
! if (!clean_ic_if_metadata_is_dead(CompiledIC_at(&iter))) {
! return false;
! }
}
! if (!clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all)) {
! return false;
! }
break;
case relocInfo::opt_virtual_call_type:
! if (!clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all)) {
! return false;
! }
break;
case relocInfo::static_call_type:
! if (!clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), this, clean_all)) {
! return false;
! }
break;
default:
break;
}
}
+
+ return true;
}
// Iterating over all nmethods, e.g. with the help of CodeCache::nmethods_do(fun) was found
// to not be inherently safe. There is a chance that fields are seen which are not properly
// initialized. This happens despite the fact that nmethods_do() asserts the CodeCache_lock
< prev index next >