OpenJDK / jdk / jdk
changeset 24442:4d4ae31dea26
8032463: VirtualDispatch test timeout with DeoptimizeALot
Summary: Introduce code aging for warm method detection
Reviewed-by: kvn, twisti
line wrap: on
line diff
--- a/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp Tue May 13 11:32:10 2014 -0700 @@ -414,6 +414,7 @@ void DeoptimizeStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); + __ set(_trap_request, G4); __ call(Runtime1::entry_for(Runtime1::deoptimize_id), relocInfo::runtime_call_type); __ delayed()->nop(); ce->add_call_info_here(_info);
--- a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp Tue May 13 11:32:10 2014 -0700 @@ -781,7 +781,7 @@ { __ set_info("deoptimize", dont_gc_arguments); OopMap* oop_map = save_live_registers(sasm); - int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize)); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize), G4); oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); restore_live_registers(sasm);
--- a/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp Tue May 13 11:32:10 2014 -0700 @@ -430,6 +430,7 @@ void DeoptimizeStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); + ce->store_parameter(_trap_request, 0); __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::deoptimize_id))); ce->add_call_info_here(_info); DEBUG_ONLY(__ should_not_reach_here());
--- a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp Tue May 13 11:32:10 2014 -0700 @@ -1468,9 +1468,10 @@ case deoptimize_id: { StubFrame f(sasm, "deoptimize", dont_gc_arguments); - const int num_rt_args = 1; // thread + const int num_rt_args = 2; // thread, trap_request OopMap* oop_map = save_live_registers(sasm, num_rt_args); - int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize)); + f.load_argument(0, rax); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize), rax); oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); restore_live_registers(sasm);
--- a/hotspot/src/share/vm/c1/c1_CodeStubs.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_CodeStubs.hpp Tue May 13 11:32:10 2014 -0700 @@ -450,9 +450,11 @@ class DeoptimizeStub : public CodeStub { private: CodeEmitInfo* _info; + jint _trap_request; public: - DeoptimizeStub(CodeEmitInfo* info) : _info(new CodeEmitInfo(info)) {} + DeoptimizeStub(CodeEmitInfo* info, Deoptimization::DeoptReason reason, Deoptimization::DeoptAction action) : + _info(new CodeEmitInfo(info)), _trap_request(Deoptimization::make_trap_request(reason, action)) {} virtual void emit_code(LIR_Assembler* e); virtual CodeEmitInfo* info() const { return _info; }
--- a/hotspot/src/share/vm/c1/c1_Compilation.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_Compilation.hpp Tue May 13 11:32:10 2014 -0700 @@ -251,6 +251,10 @@ return env()->comp_level() == CompLevel_full_profile && C1UpdateMethodData && MethodData::profile_return(); } + bool age_code() const { + return _method->profile_aging(); + } + // will compilation make optimistic assumptions that might lead to // deoptimization and that the runtime will account for? bool is_optimistic() const {
--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Tue May 13 11:32:10 2014 -0700 @@ -2782,7 +2782,10 @@ __ lock_object(syncTempOpr(), obj, lock, new_register(T_OBJECT), slow_path, NULL); } } - + if (compilation()->age_code()) { + CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, 0), NULL, false); + decrement_age(info); + } // increment invocation counters if needed if (!method()->is_accessor()) { // Accessors do not have MDOs, so no counting. profile_parameters(x); @@ -3328,6 +3331,27 @@ increment_event_counter_impl(info, info->scope()->method(), (1 << freq_log) - 1, bci, backedge, true); } +void LIRGenerator::decrement_age(CodeEmitInfo* info) { + ciMethod* method = info->scope()->method(); + MethodCounters* mc_adr = method->ensure_method_counters(); + if (mc_adr != NULL) { + LIR_Opr mc = new_pointer_register(); + __ move(LIR_OprFact::intptrConst(mc_adr), mc); + int offset = in_bytes(MethodCounters::nmethod_age_offset()); + LIR_Address* counter = new LIR_Address(mc, offset, T_INT); + LIR_Opr result = new_register(T_INT); + __ load(counter, result); + __ sub(result, LIR_OprFact::intConst(1), result); + __ store(result, counter); + // DeoptimizeStub will reexecute from the current state in code info. + CodeStub* deopt = new DeoptimizeStub(info, Deoptimization::Reason_tenured, + Deoptimization::Action_make_not_entrant); + __ cmp(lir_cond_lessEqual, result, LIR_OprFact::intConst(0)); + __ branch(lir_cond_lessEqual, T_INT, deopt); + } +} + + void LIRGenerator::increment_event_counter_impl(CodeEmitInfo* info, ciMethod *method, int frequency, int bci, bool backedge, bool notify) {
--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp Tue May 13 11:32:10 2014 -0700 @@ -372,7 +372,7 @@ increment_event_counter(info, bci, true); } } - + void decrement_age(CodeEmitInfo* info); CodeEmitInfo* state_for(Instruction* x, ValueStack* state, bool ignore_xhandler = false); CodeEmitInfo* state_for(Instruction* x);
--- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp Tue May 13 11:32:10 2014 -0700 @@ -685,19 +685,32 @@ JRT_END // Cf. OptoRuntime::deoptimize_caller_frame -JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* thread)) +JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* thread, jint trap_request)) // Called from within the owner thread, so no need for safepoint RegisterMap reg_map(thread, false); frame stub_frame = thread->last_frame(); - assert(stub_frame.is_runtime_frame(), "sanity check"); + assert(stub_frame.is_runtime_frame(), "Sanity check"); frame caller_frame = stub_frame.sender(®_map); + nmethod* nm = caller_frame.cb()->as_nmethod_or_null(); + assert(nm != NULL, "Sanity check"); + methodHandle method(thread, nm->method()); + assert(nm == CodeCache::find_nmethod(caller_frame.pc()), "Should be the same"); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(trap_request); + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(trap_request); - // We are coming from a compiled method; check this is true. - assert(CodeCache::find_nmethod(caller_frame.pc()) != NULL, "sanity"); + if (action == Deoptimization::Action_make_not_entrant) { + if (nm->make_not_entrant()) { + if (reason == Deoptimization::Reason_tenured) { + MethodData* trap_mdo = Deoptimization::get_method_data(thread, method, true /*create_if_missing*/); + if (trap_mdo != NULL) { + trap_mdo->inc_tenure_traps(); + } + } + } + } // Deoptimize the caller frame. Deoptimization::deoptimize_frame(thread, caller_frame.id()); - // Return to the now deoptimized frame. JRT_END
--- a/hotspot/src/share/vm/c1/c1_Runtime1.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/c1/c1_Runtime1.hpp Tue May 13 11:32:10 2014 -0700 @@ -156,7 +156,7 @@ static void monitorenter(JavaThread* thread, oopDesc* obj, BasicObjectLock* lock); static void monitorexit (JavaThread* thread, BasicObjectLock* lock); - static void deoptimize(JavaThread* thread); + static void deoptimize(JavaThread* thread, jint trap_request); static int access_field_patching(JavaThread* thread); static int move_klass_patching(JavaThread* thread);
--- a/hotspot/src/share/vm/ci/ciMethod.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/ci/ciMethod.cpp Tue May 13 11:32:10 2014 -0700 @@ -129,6 +129,7 @@ constantPoolHandle cpool = h_m()->constants(); _signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol); _method_data = NULL; + _nmethod_age = h_m()->nmethod_age(); // Take a snapshot of these values, so they will be commensurate with the MDO. if (ProfileInterpreter || TieredCompilation) { int invcnt = h_m()->interpreter_invocation_count(); @@ -1276,6 +1277,14 @@ } // ------------------------------------------------------------------ +// ciMethod::profile_aging +// +// Should the method be compiled with an age counter? +bool ciMethod::profile_aging() const { + return UseCodeAging && (!MethodCounters::is_nmethod_hot(nmethod_age()) && + !MethodCounters::is_nmethod_age_unset(nmethod_age())); +} +// ------------------------------------------------------------------ // ciMethod::print_codes // // Print the bytecodes for this method.
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/ci/ciMethod.hpp Tue May 13 11:32:10 2014 -0700 @@ -68,6 +68,7 @@ int _max_locals; vmIntrinsics::ID _intrinsic_id; int _handler_count; + int _nmethod_age; int _interpreter_invocation_count; int _interpreter_throwout_count; int _instructions_size; @@ -168,6 +169,10 @@ int interpreter_invocation_count() const { check_is_loaded(); return _interpreter_invocation_count; } int interpreter_throwout_count() const { check_is_loaded(); return _interpreter_throwout_count; } int size_of_parameters() const { check_is_loaded(); return _size_of_parameters; } + int nmethod_age() const { check_is_loaded(); return _nmethod_age; } + + // Should the method be compiled with an age counter? + bool profile_aging() const; // Code size for inlining decisions. int code_size_for_inlining();
--- a/hotspot/src/share/vm/oops/method.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/oops/method.hpp Tue May 13 11:32:10 2014 -0700 @@ -371,6 +371,13 @@ } } #endif + int nmethod_age() const { + if (method_counters() == NULL) { + return INT_MAX; + } else { + return method_counters()->nmethod_age(); + } + } int invocation_count(); int backedge_count();
--- a/hotspot/src/share/vm/oops/methodCounters.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/oops/methodCounters.cpp Tue May 13 11:32:10 2014 -0700 @@ -34,4 +34,5 @@ backedge_counter()->reset(); set_interpreter_throwout_count(0); set_interpreter_invocation_count(0); + set_nmethod_age(INT_MAX); }
--- a/hotspot/src/share/vm/oops/methodCounters.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/oops/methodCounters.hpp Tue May 13 11:32:10 2014 -0700 @@ -36,6 +36,15 @@ u2 _number_of_breakpoints; // fullspeed debugging support InvocationCounter _invocation_counter; // Incremented before each activation of the method - used to trigger frequency-based optimizations InvocationCounter _backedge_counter; // Incremented before each backedge taken - used to trigger frequencey-based optimizations + // NMethod age is a counter for warm methods detection in the code cache sweeper. + // The counter is reset by the sweeper and is decremented by some of the compiled + // code. The counter values are interpreted as follows: + // 1. (HotMethodDetection..INT_MAX] - initial value, no counters inserted + // 2. (1..HotMethodDetectionLimit) - the method is warm, the counter is used + // to figure out which methods can be flushed. + // 3. (INT_MIN..0] - method is hot and will deopt and get + // recompiled without the counters + int _nmethod_age; #ifdef TIERED float _rate; // Events (invocation and backedge counter increments) per millisecond @@ -44,7 +53,8 @@ MethodCounters() : _interpreter_invocation_count(0), _interpreter_throwout_count(0), - _number_of_breakpoints(0) + _number_of_breakpoints(0), + _nmethod_age(INT_MAX) #ifdef TIERED , _rate(0), _prev_time(0) @@ -52,6 +62,10 @@ { invocation_counter()->init(); backedge_counter()->init(); + + if (StressCodeAging) { + set_nmethod_age(HotMethodDetectionLimit); + } } public: @@ -104,6 +118,24 @@ InvocationCounter* invocation_counter() { return &_invocation_counter; } InvocationCounter* backedge_counter() { return &_backedge_counter; } + int nmethod_age() { + return _nmethod_age; + } + void set_nmethod_age(int age) { + _nmethod_age = age; + } + void reset_nmethod_age() { + set_nmethod_age(HotMethodDetectionLimit); + } + + static bool is_nmethod_hot(int age) { return age <= 0; } + static bool is_nmethod_warm(int age) { return age < HotMethodDetectionLimit; } + static bool is_nmethod_age_unset(int age) { return age > HotMethodDetectionLimit; } + + static ByteSize nmethod_age_offset() { + return byte_offset_of(MethodCounters, _nmethod_age); + } + static ByteSize interpreter_invocation_counter_offset() { return byte_offset_of(MethodCounters, _interpreter_invocation_count); }
--- a/hotspot/src/share/vm/oops/methodData.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/oops/methodData.cpp Tue May 13 11:32:10 2014 -0700 @@ -1130,6 +1130,7 @@ _backedge_counter.init(); _invocation_counter_start = 0; _backedge_counter_start = 0; + _tenure_traps = 0; _num_loops = 0; _num_blocks = 0; _highest_comp_level = 0;
--- a/hotspot/src/share/vm/oops/methodData.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/oops/methodData.hpp Tue May 13 11:32:10 2014 -0700 @@ -2059,6 +2059,7 @@ // Counter values at the time profiling started. int _invocation_counter_start; int _backedge_counter_start; + uint _tenure_traps; #if INCLUDE_RTM_OPT // State of RTM code generation during compilation of the method @@ -2398,6 +2399,12 @@ method()->set_not_compilable(CompLevel_full_optimization, true, "decompile_count > PerMethodRecompilationCutoff"); } } + uint tenure_traps() const { + return _tenure_traps; + } + void inc_tenure_traps() { + _tenure_traps += 1; + } // Return pointer to area dedicated to parameters in MDO ParametersTypeData* parameters_type_data() const {
--- a/hotspot/src/share/vm/opto/compile.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/opto/compile.cpp Tue May 13 11:32:10 2014 -0700 @@ -1089,6 +1089,7 @@ set_do_scheduling(OptoScheduling); set_do_count_invocations(false); set_do_method_data_update(false); + set_age_code(has_method() && method()->profile_aging()); set_rtm_state(NoRTM); // No RTM lock eliding by default #if INCLUDE_RTM_OPT if (UseRTMLocking && has_method() && (method()->method_data_or_null() != NULL)) {
--- a/hotspot/src/share/vm/opto/compile.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/opto/compile.hpp Tue May 13 11:32:10 2014 -0700 @@ -311,6 +311,7 @@ bool _do_freq_based_layout; // True if we intend to do frequency based block layout bool _do_count_invocations; // True if we generate code to count invocations bool _do_method_data_update; // True if we generate code to update MethodData*s + bool _age_code; // True if we need to profile code age (decrement the aging counter) int _AliasLevel; // Locally-adjusted version of AliasLevel flag. bool _print_assembly; // True if we should dump assembly code for this compilation bool _print_inlining; // True if we should print inlining for this compilation @@ -584,7 +585,9 @@ void set_do_count_invocations(bool z){ _do_count_invocations = z; } bool do_method_data_update() const { return _do_method_data_update; } void set_do_method_data_update(bool z) { _do_method_data_update = z; } - int AliasLevel() const { return _AliasLevel; } + bool age_code() const { return _age_code; } + void set_age_code(bool z) { _age_code = z; } + int AliasLevel() const { return _AliasLevel; } bool print_assembly() const { return _print_assembly; } void set_print_assembly(bool z) { _print_assembly = z; } bool print_inlining() const { return _print_inlining; }
--- a/hotspot/src/share/vm/opto/parse.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/opto/parse.hpp Tue May 13 11:32:10 2014 -0700 @@ -581,6 +581,7 @@ void jump_switch_ranges(Node* a, SwitchRange* lo, SwitchRange* hi, int depth = 0); bool create_jump_tables(Node* a, SwitchRange* lo, SwitchRange* hi); + void decrement_age(); // helper functions for methodData style profiling void test_counter_against_threshold(Node* cnt, int limit); void increment_and_test_invocation_counter(int limit);
--- a/hotspot/src/share/vm/opto/parse1.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/opto/parse1.cpp Tue May 13 11:32:10 2014 -0700 @@ -568,6 +568,9 @@ } else { set_map(entry_map); do_method_entry(); + if (depth() == 1 && C->age_code()) { + decrement_age(); + } } if (depth() == 1) { // Add check to deoptimize the nmethod if RTM state was changed @@ -2048,6 +2051,31 @@ #endif } +void Parse::decrement_age() { + MethodCounters* mc = method()->ensure_method_counters(); + if (mc == NULL) { + C->record_failure("Must have MCs"); + return; + } + assert(!is_osr_parse(), "Not doing this for OSRs"); + + // Set starting bci for uncommon trap. + set_parse_bci(0); + + const TypePtr* adr_type = TypeRawPtr::make((address)mc); + Node* mc_adr = makecon(adr_type); + Node* cnt_adr = basic_plus_adr(mc_adr, mc_adr, in_bytes(MethodCounters::nmethod_age_offset())); + Node* cnt = make_load(control(), cnt_adr, TypeInt::INT, T_INT, adr_type, MemNode::unordered); + Node* decr = _gvn.transform(new (C) SubINode(cnt, makecon(TypeInt::ONE))); + store_to_memory(control(), cnt_adr, decr, T_INT, adr_type, MemNode::unordered); + Node *chk = _gvn.transform(new (C) CmpINode(decr, makecon(TypeInt::ZERO))); + Node* tst = _gvn.transform(new (C) BoolNode(chk, BoolTest::gt)); + { BuildCutout unless(this, tst, PROB_ALWAYS); + uncommon_trap(Deoptimization::Reason_tenured, + Deoptimization::Action_make_not_entrant); + } +} + //------------------------------return_current--------------------------------- // Append current _map to _exit_return void Parse::return_current(Node* value) {
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue May 13 11:32:10 2014 -0700 @@ -744,6 +744,8 @@ return 0; } +Deoptimization::DeoptAction Deoptimization::_unloaded_action + = Deoptimization::Action_reinterpret; #ifdef COMPILER2 bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS) { @@ -1185,6 +1187,23 @@ } JRT_END +MethodData* +Deoptimization::get_method_data(JavaThread* thread, methodHandle m, + bool create_if_missing) { + Thread* THREAD = thread; + MethodData* mdo = m()->method_data(); + if (mdo == NULL && create_if_missing && !HAS_PENDING_EXCEPTION) { + // Build an MDO. Ignore errors like OutOfMemory; + // that simply means we won't have an MDO to update. + Method::build_interpreter_method_data(m, THREAD); + if (HAS_PENDING_EXCEPTION) { + assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); + CLEAR_PENDING_EXCEPTION; + } + mdo = m()->method_data(); + } + return mdo; +} #if defined(COMPILER2) || defined(SHARK) void Deoptimization::load_class_by_index(constantPoolHandle constant_pool, int index, TRAPS) { @@ -1285,7 +1304,7 @@ // Ensure that we can record deopt. history: // Need MDO to record RTM code generation state. - bool create_if_missing = ProfileTraps RTM_OPT_ONLY( || UseRTMLocking ); + bool create_if_missing = ProfileTraps || UseCodeAging RTM_OPT_ONLY( || UseRTMLocking ); MethodData* trap_mdo = get_method_data(thread, trap_method, create_if_missing); @@ -1421,7 +1440,7 @@ // // The other actions cause immediate removal of the present code. - bool update_trap_state = true; + bool update_trap_state = (reason != Reason_tenured); bool make_not_entrant = false; bool make_not_compilable = false; bool reprofile = false; @@ -1548,7 +1567,6 @@ if (make_not_entrant && maybe_prior_recompile && maybe_prior_trap) { reprofile = true; } - } // Take requested actions on the method: @@ -1577,6 +1595,11 @@ trap_mdo->atomic_set_rtm_state(ProfileRTM); } #endif + // For code aging we count traps separately here, using make_not_entrant() + // as a guard against simultaneous deopts in multiple threads. + if (reason == Reason_tenured && trap_mdo != NULL) { + trap_mdo->inc_tenure_traps(); + } } if (inc_recompile_count) { @@ -1609,24 +1632,6 @@ } JRT_END -MethodData* -Deoptimization::get_method_data(JavaThread* thread, methodHandle m, - bool create_if_missing) { - Thread* THREAD = thread; - MethodData* mdo = m()->method_data(); - if (mdo == NULL && create_if_missing && !HAS_PENDING_EXCEPTION) { - // Build an MDO. Ignore errors like OutOfMemory; - // that simply means we won't have an MDO to update. - Method::build_interpreter_method_data(m, THREAD); - if (HAS_PENDING_EXCEPTION) { - assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); - CLEAR_PENDING_EXCEPTION; - } - mdo = m()->method_data(); - } - return mdo; -} - ProfileData* Deoptimization::query_update_method_data(MethodData* trap_mdo, int trap_bci, @@ -1813,8 +1818,6 @@ //--------------------------------statics-------------------------------------- -Deoptimization::DeoptAction Deoptimization::_unloaded_action - = Deoptimization::Action_reinterpret; const char* Deoptimization::_trap_reason_name[Reason_LIMIT] = { // Note: Keep this in sync. with enum DeoptReason. "none",
--- a/hotspot/src/share/vm/runtime/deoptimization.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/deoptimization.hpp Tue May 13 11:32:10 2014 -0700 @@ -62,6 +62,7 @@ Reason_speculate_class_check, // saw unexpected object class from type speculation Reason_speculate_null_check, // saw unexpected null from type speculation Reason_rtm_state_change, // rtm state change detected + Reason_tenured, // age of the code has reached the limit Reason_LIMIT, // Note: Keep this enum in sync. with _trap_reason_name. Reason_RECORDED_LIMIT = Reason_bimorphic // some are not recorded per bc @@ -357,8 +358,8 @@ // returning to a deoptimized caller static void popframe_preserve_args(JavaThread* thread, int bytes_to_save, void* start_address); + static MethodData* get_method_data(JavaThread* thread, methodHandle m, bool create_if_missing); private: - static MethodData* get_method_data(JavaThread* thread, methodHandle m, bool create_if_missing); // Update the mdo's count and per-BCI reason bits, returning previous state: static ProfileData* query_update_method_data(MethodData* trap_mdo, int trap_bci,
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/globals.hpp Tue May 13 11:32:10 2014 -0700 @@ -2561,6 +2561,20 @@ diagnostic(bool, PrintMethodFlushingStatistics, false, \ "print statistics about method flushing") \ \ + diagnostic(intx, HotMethodDetectionLimit, 100000, \ + "Number of compiled code invocations after which " \ + "the method is considered as hot by the flusher") \ + \ + diagnostic(intx, MinPassesBeforeFlush, 10, \ + "Minimum number of sweeper passes before an nmethod " \ + "can be flushed") \ + \ + product(bool, UseCodeAging, true, \ + "Insert counter to detect warm methods") \ + \ + diagnostic(bool, StressCodeAging, false, \ + "Start with counters compiled in") \ + \ develop(bool, UseRelocIndex, false, \ "Use an index to speed random access to relocations") \ \
--- a/hotspot/src/share/vm/runtime/sweeper.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/sweeper.cpp Tue May 13 11:32:10 2014 -0700 @@ -573,37 +573,7 @@ SWEEP(nm); } } else { - if (UseCodeCacheFlushing) { - if (!nm->is_locked_by_vm() && !nm->is_osr_method() && !nm->is_native_method()) { - // Do not make native methods and OSR-methods not-entrant - nm->dec_hotness_counter(); - // Get the initial value of the hotness counter. This value depends on the - // ReservedCodeCacheSize - int reset_val = hotness_counter_reset_val(); - int time_since_reset = reset_val - nm->hotness_counter(); - double threshold = -reset_val + (CodeCache::reverse_free_ratio() * NmethodSweepActivity); - // The less free space in the code cache we have - the bigger reverse_free_ratio() is. - // I.e., 'threshold' increases with lower available space in the code cache and a higher - // NmethodSweepActivity. If the current hotness counter - which decreases from its initial - // value until it is reset by stack walking - is smaller than the computed threshold, the - // corresponding nmethod is considered for removal. - if ((NmethodSweepActivity > 0) && (nm->hotness_counter() < threshold) && (time_since_reset > 10)) { - // A method is marked as not-entrant if the method is - // 1) 'old enough': nm->hotness_counter() < threshold - // 2) The method was in_use for a minimum amount of time: (time_since_reset > 10) - // The second condition is necessary if we are dealing with very small code cache - // sizes (e.g., <10m) and the code cache size is too small to hold all hot methods. - // The second condition ensures that methods are not immediately made not-entrant - // after compilation. - nm->make_not_entrant(); - // Code cache state change is tracked in make_not_entrant() - if (PrintMethodFlushing && Verbose) { - tty->print_cr("### Nmethod %d/" PTR_FORMAT "made not-entrant: hotness counter %d/%d threshold %f", - nm->compile_id(), nm, nm->hotness_counter(), reset_val, threshold); - } - } - } - } + possibly_flush(nm); // Clean-up all inline caches that point to zombie/non-reentrant methods MutexLocker cl(CompiledIC_lock); nm->cleanup_inline_caches(); @@ -612,6 +582,88 @@ return freed_memory; } + +void NMethodSweeper::possibly_flush(nmethod* nm) { + if (UseCodeCacheFlushing) { + if (!nm->is_locked_by_vm() && !nm->is_osr_method() && !nm->is_native_method()) { + bool make_not_entrant = false; + + // Do not make native methods and OSR-methods not-entrant + nm->dec_hotness_counter(); + // Get the initial value of the hotness counter. This value depends on the + // ReservedCodeCacheSize + int reset_val = hotness_counter_reset_val(); + int time_since_reset = reset_val - nm->hotness_counter(); + double threshold = -reset_val + (CodeCache::reverse_free_ratio() * NmethodSweepActivity); + // The less free space in the code cache we have - the bigger reverse_free_ratio() is. + // I.e., 'threshold' increases with lower available space in the code cache and a higher + // NmethodSweepActivity. If the current hotness counter - which decreases from its initial + // value until it is reset by stack walking - is smaller than the computed threshold, the + // corresponding nmethod is considered for removal. + if ((NmethodSweepActivity > 0) && (nm->hotness_counter() < threshold) && (time_since_reset > MinPassesBeforeFlush)) { + // A method is marked as not-entrant if the method is + // 1) 'old enough': nm->hotness_counter() < threshold + // 2) The method was in_use for a minimum amount of time: (time_since_reset > MinPassesBeforeFlush) + // The second condition is necessary if we are dealing with very small code cache + // sizes (e.g., <10m) and the code cache size is too small to hold all hot methods. + // The second condition ensures that methods are not immediately made not-entrant + // after compilation. + make_not_entrant = true; + } + + // The stack-scanning low-cost detection may not see the method was used (which can happen for + // flat profiles). Check the age counter for possible data. + if (UseCodeAging && make_not_entrant && (nm->is_compiled_by_c2() || nm->is_compiled_by_c1())) { + MethodCounters* mc = nm->method()->method_counters(); + if (mc != NULL) { + // Snapshot the value as it's changed concurrently + int age = mc->nmethod_age(); + if (MethodCounters::is_nmethod_hot(age)) { + // The method has gone through flushing, and it became relatively hot that it deopted + // before we could take a look at it. Give it more time to appear in the stack traces, + // proportional to the number of deopts. + MethodData* md = nm->method()->method_data(); + if (md != NULL && time_since_reset > (int)(MinPassesBeforeFlush * (md->tenure_traps() + 1))) { + // It's been long enough, we still haven't seen it on stack. + // Try to flush it, but enable counters the next time. + mc->reset_nmethod_age(); + } else { + make_not_entrant = false; + } + } else if (MethodCounters::is_nmethod_warm(age)) { + // Method has counters enabled, and the method was used within + // previous MinPassesBeforeFlush sweeps. Reset the counter. Stay in the existing + // compiled state. + mc->reset_nmethod_age(); + // delay the next check + nm->set_hotness_counter(NMethodSweeper::hotness_counter_reset_val()); + make_not_entrant = false; + } else if (MethodCounters::is_nmethod_age_unset(age)) { + // No counters were used before. Set the counters to the detection + // limit value. If the method is going to be used again it will be compiled + // with counters that we're going to use for analysis the the next time. + mc->reset_nmethod_age(); + } else { + // Method was totally idle for 10 sweeps + // The counter already has the initial value, flush it and may be recompile + // later with counters + } + } + } + + if (make_not_entrant) { + nm->make_not_entrant(); + + // Code cache state change is tracked in make_not_entrant() + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Nmethod %d/" PTR_FORMAT "made not-entrant: hotness counter %d/%d threshold %f", + nm->compile_id(), nm, nm->hotness_counter(), reset_val, threshold); + } + } + } + } +} + // Print out some state information about the current sweep and the // state of the code cache if it's requested. void NMethodSweeper::log_sweep(const char* msg, const char* format, ...) {
--- a/hotspot/src/share/vm/runtime/sweeper.hpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/sweeper.hpp Tue May 13 11:32:10 2014 -0700 @@ -111,6 +111,7 @@ static int hotness_counter_reset_val(); static void report_state_change(nmethod* nm); static void possibly_enable_sweeper(); + static void possibly_flush(nmethod* nm); static void print(); // Printing/debugging };
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Tue May 13 11:25:17 2014 +0200 +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Tue May 13 11:32:10 2014 -0700 @@ -361,10 +361,12 @@ nonstatic_field(MethodData, _arg_local, intx) \ nonstatic_field(MethodData, _arg_stack, intx) \ nonstatic_field(MethodData, _arg_returned, intx) \ + nonstatic_field(MethodData, _tenure_traps, uint) \ nonstatic_field(DataLayout, _header._struct._tag, u1) \ nonstatic_field(DataLayout, _header._struct._flags, u1) \ nonstatic_field(DataLayout, _header._struct._bci, u2) \ nonstatic_field(DataLayout, _cells[0], intptr_t) \ + nonstatic_field(MethodCounters, _nmethod_age, int) \ nonstatic_field(MethodCounters, _interpreter_invocation_count, int) \ nonstatic_field(MethodCounters, _interpreter_throwout_count, u2) \ nonstatic_field(MethodCounters, _number_of_breakpoints, u2) \