Preliminary review for 8028107: Kitchensink crashed with EAV
Vladimir Kozlov
vladimir.kozlov at oracle.com
Wed Nov 27 17:51:28 PST 2013
https://bugs.openjdk.java.net/browse/JDK-8028107
It is very rare case. Last time it was 1 year ago:
https://bugs.openjdk.java.net/browse/JDK-7199505
nmethod unloading (nmethod::make_unloaded()) happens only during
safepoint when it contains pointers (embedded or in oop map, relocation
info) to dead java objects (see nmethod::can_unload()).
It can only happens when a nmethod is NOT on call stack. GCs have path
over nmethods on stack and they are treated as strong roots. Igor and I
looked on GC code and verified it.
I instrumented nmethod::make_unloaded() to see if we may unloading
nmethods on stack and ran kitchensink for few day. No failures.
The only place left is compiled inline caches. We DO clean up them at
the end of safepoint if unloading happens:
if (is_in_use()) {
// Transitioning directly from live to unloaded -- so
// we need to force a cache clean-up; remember this
// for later on.
CodeCache::set_needs_cache_clean(true);
}
But there is only place where we may create new compiledIC after
safepoint when we are resolving call from compiled code:
SharedRuntime::resolve_sub_helper().
In this method we record nmethod in local var:
CompiledICInfo virtual_call_info;
CompiledIC::compute_monomorphic_entry(callee_method, h_klass,
is_optimized, static_bound, virtual_call_info,
CHECK_(methodHandle()));
then we take a lock (which is safepoint):
// grab lock, check for deoptimization and potentially patch caller
{
MutexLocker ml_patch(CompiledIC_lock);
and then create compiledIC based on info in CompiledICInfo:
CompiledIC* inline_cache = CompiledIC_before(caller_nm,
caller_frame.pc());
if (inline_cache->is_clean()) {
inline_cache->set_to_monomorphic(virtual_call_info);
}
at this point nmethod could be unloaded already during above
lock/safepoint. But call site is patched and will be used to call this
nmethod.
Note, we can't compute entry point under the same lock because:
// Compute entry points. This might require generation of C2I converter
// frames, so we cannot be holding any locks here. Furthermore, the
// computation of the entry points is independent of patching the call.
Please, comment or find any flaws in my logic. The bug is impossible to
reproduce.
I am suggesting next fix.
src/share/vm/runtime/sharedRuntime.cpp
@@ -1258,12 +1258,14 @@
// Now that we are ready to patch if the Method* was redefined then
// don't update call site and let the caller retry.
-
- if (!callee_method->is_old()) {
+ // Don't update call site if nmethod was unloaded during safepoint
+ // which may happened during locking above.
+ if (!callee_method->is_old() && (callee_method->code() == callee_nm)) {
#ifdef ASSERT
// We must not try to patch to jump to an already unloaded method.
if (dest_entry_point != 0) {
- assert(CodeCache::find_blob(dest_entry_point) != NULL,
+ CodeBlob* cb = CodeCache::find_blob(dest_entry_point);
+ assert((cb != NULL) && cb->is_nmethod() &&
((nmethod*)cb)->is_in_use(),
"should not unload nmethod while locked");
}
#endif
Thanks,
Vladimir
More information about the hotspot-compiler-dev
mailing list