Request review: 4360113: Evict nmethods when code cache gets full

Eric Caspole eric.caspole at amd.com
Wed Dec 16 10:40:05 PST 2009


Hi Vladimir,
Thanks for your comments and trying to run it. More comments below.
Eric

On Dec 15, 2009, at 5:25 PM, Vladimir Kozlov wrote:

> Eric,
>
> In compileBroker.cpp and sweeper.cpp, I think, you should use separate
> new flag instead of 3*CodeCacheMinimumFreeSpace so it could
> be adjusted separatly.

OK. I am running out of good names, how about  
CodeCacheFlushingTriggerFreeSpace?

>
> Also you can move new condition after the body of the original
> so you will not need to check "> CodeCacheMinimumFreeSpace".
>
> I looked on verified_code_entry() and it works since
> clear_code() stores _adapter->get_c2i_entry() into
> _from_compiled_entry so it is not NULL.
>
> In handle_full_code_cache() do "if(!is_full) {} else {}"
> Change print format for "is_full".
> Change print format for for jint values (%ld% is for longs).
> Use INT64_FORMAT instead of %ld.

Will do.

>
> I am not sure the next code is correct.
> What if method was compiled again and got new nmethod?

I am not sure what you mean about compiled again, but I think the  
method would not be selected for flushing in that case.

> Also you should not reset all methodOop values (see failures below):

See below

>
> +         if ((((nmethod*)nm)->method()->code() == nm)) {
> +           // This method has not been previously considered for  
> preemptive unloading
> +           // or it was restored once already
> +           ((nmethod*)nm)->method()->clear_code_hedge();
> +         } else {
> +           // This method was previously considered for preemptive  
> unloading and was not called since then
> +           ((nmethod*)nm)->method()->set_saved_code(NULL);
> +           ((nmethod*)nm)->method()->invocation_counter()->reset();
> +           ((nmethod*)nm)->method()->backedge_counter()->reset();
> +           ((nmethod*)nm)->method()- 
> >set_interpreter_invocation_count(0);
> +           ((nmethod*)nm)->method()->set_method_data(NULL);
> +           ((nmethod*)nm)->make_not_entrant();
> +         }
>
> When I ran fastdebug VM with your patch on MagnyCours machine I got  
> next failures.
>
> Thanks,
> Vladimir
>
>

Yes I got carried away here erasing the mdo. I saw other places can  
reallocate mdo if necessary, but not here. The effect I was going for  
was to slow the stampede of recompiles. I will change this and retest  
it.

> % java -XX:ReservedCodeCacheSize=6M -XX:+PrintCodeCacheExtension - 
> XX:+UseCodeCacheFlushing -XX:MinCodeCacheFlushingInterval=300 -XX: 
> +PrintCompilation -jar SPECjvm2008.jar -ikv compiler.compiler
>
> #  Internal Error (/net/irkutsk/export/home/kvn/work2/hg/4360113/ 
> src/share/vm/runtime/frame.cpp:373), pid=26211, tid=35
> #  Error: assert(mdo != 0,"")
>
> V  [libjvm.so+0x170231a] void VMError::report(outputStream*) + 0x5be
> V  [libjvm.so+0x17032f6] void VMError::report_and_die() + 0x586
> V  [libjvm.so+0x7bf93d] void report_assertion_failure(const  
> char*,int,const char*) + 0x61
> V  [libjvm.so+0x88f534] void frame::interpreter_frame_set_bcx(int)  
> + 0x254
> V  [libjvm.so+0x894888] void frame::gc_prologue() + 0x3c
> V  [libjvm.so+0x1616292] void frame_gc_prologue(frame*,const  
> RegisterMap*) + 0x1e
> V  [libjvm.so+0x1615c28] void JavaThread::frames_do(void(*) 
> (frame*,const RegisterMap*)) + 0xa4
> V  [libjvm.so+0x16162c1] void JavaThread::gc_prologue() + 0x25
> V  [libjvm.so+0x161e6ba] void Threads::gc_prologue() + 0x26
> V  [libjvm.so+0x139bf91] void PSMarkSweep::invoke_no_policy(bool) +  
> 0x46d
> V  [libjvm.so+0x13c7eda] void PSScavenge::invoke() + 0x1da
> V  [libjvm.so+0x1317b15]  
> HeapWord*ParallelScavengeHeap::failed_mem_allocate(unsigned,bool) +  
> 0x115
> V  [libjvm.so+0x1704fa1] void VM_ParallelGCFailedAllocation::doit()  
> + 0xc5
> V  [libjvm.so+0x1737c89] void VM_Operation::evaluate() + 0xe1
> V  [libjvm.so+0x1736457] void VMThread::evaluate_operation 
> (VM_Operation*) + 0
>
>

The one below looks like a too eager assert in  
SimpleCompPolicy::method_invocation_event(), since the next if block  
below the assert again checks "&& UseCompiler)" -- what do you think?


> % java -XX:ReservedCodeCacheSize=6M -XX:+PrintCodeCacheExtension - 
> XX:+UseCodeCacheFlushing -XX:MinCodeCacheFlushingInterval=300 -XX: 
> +PrintCompilation -XX:+PrintMethodFlushing -jar SPECjvm2008.jar - 
> ikv compiler.sunflow compiler.compiler
>
> #  Internal Error (/net/irkutsk/export/home/kvn/work2/hg/4360113/ 
> src/share/vm/runtime/compilationPolicy.cpp:124), pid=26531, tid=51
> #  Error: assert(UseCompiler || CompileTheWorld,"UseCompiler should  
> be set by now.")
>
> Current thread (0x08c84400):  JavaThread "BenchmarkThread  
> compiler.sunflow 6" [_thread_in_vm, id=51, stack 
> (0xfa2c9000,0xfa319000)]
>
> Stack: [0xfa2c9000,0xfa319000],  sp=0xfa317ad8,  free  
> space=13afa319000k
> Native frames: (J=compiled Java code, j=interpreted, Vv=VM code,  
> C=native code)
> V  [libjvm.so+0x170235a] void VMError::report(outputStream*) + 0x5be
> V  [libjvm.so+0x1703336] void VMError::report_and_die() + 0x586
> V  [libjvm.so+0x7bf95d] void report_assertion_failure(const  
> char*,int,const char*) + 0x61
> V  [libjvm.so+0x6ccd4e] void  
> SimpleCompPolicy::method_invocation_event(methodHandle,Thread*) +  
> 0x20a
> V  [libjvm.so+0xa68949]  
> nmethod*InterpreterRuntime::frequency_counter_overflow_inner 
> (JavaThread*,unsigned char*) + 0x1551
>
>
> Eric Caspole wrote:
>> 4360113: Evict nmethods when code cache gets full
>> http://cr.openjdk.java.net/~ecaspole/4360113/
>> In this change, under a flag and off by default, when compilers  
>> notice the code cache is getting full, they will call a vm op that  
>> calls new code that attempts to speculatively unload the oldest  
>> half of the nmethods (based on the compile job id) by hiding the  
>> methodOop's ref to the nmethod in the new _saved_code field. Then  
>> execution resumes. After inline cache cleaning, callers will have  
>> to go back to the methodOop to get the verified entry point.
>> At that point, the methodOop's _code field is restored from the  
>> _saved_code field and the methodOop/nmethod go back to their  
>> normal state.
>> If a method so marked is not called by the second sweep cycle  
>> after the one where forced unloading happened, the nmethod will be  
>> marked non-entrant and got rid of by normal sweeping. That gives  
>> the app a few seconds to make progress and call its hottest methods.
>> We chose to target the oldest half of nmethods due to a customer  
>> experience with a long-running app server, and despite multiple  
>> redeployments of the same web app, something was preventing old  
>> instances of the web app from ever getting unloaded. In that case,  
>> they ran into the code cache full problem so the most recent  
>> redeployment was running interpreter only. We have also observed  
>> that for many applications a lot of methods get compiled and used  
>> during the startup phase that are never used again.
>> In this change there is also a timer based backoff, default of 30  
>> seconds, so that if the normal state of the app is constantly  
>> triggering unloading, the unloading will stop and it will fall  
>> back to the existing situation of disabling the compiler.
>> In my testing, this allows the program to quickly resume normal  
>> operation with no noticeable performance degradation.
>> Thanks for your comments,
>> Eric
>




More information about the hotspot-compiler-dev mailing list