Preliminary review for 8028107: Kitchensink crashed with EAV

Igor Veresov igor.veresov at oracle.com
Thu Nov 28 17:45:29 PST 2013


Cool! Wow, that’s a very nice find. I wonder if that the only place this happens…

igor

On Nov 27, 2013, at 5:51 PM, Vladimir Kozlov <vladimir.kozlov at oracle.com> wrote:

> 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