changeset 6451:975d903e1de3

8032463: VirtualDispatch test timeout with DeoptimizeALot Summary: Introduce code aging for warm method detection Reviewed-by: kvn, twisti
author iveresov
date Tue, 13 May 2014 11:32:10 -0700
parents 3d247564e2d8
children 99dc0ff1d4c7
files src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp src/cpu/sparc/vm/c1_Runtime1_sparc.cpp src/cpu/x86/vm/c1_CodeStubs_x86.cpp src/cpu/x86/vm/c1_Runtime1_x86.cpp src/share/vm/c1/c1_CodeStubs.hpp src/share/vm/c1/c1_Compilation.hpp src/share/vm/c1/c1_LIRGenerator.cpp src/share/vm/c1/c1_LIRGenerator.hpp src/share/vm/c1/c1_Runtime1.cpp src/share/vm/c1/c1_Runtime1.hpp src/share/vm/ci/ciMethod.cpp src/share/vm/ci/ciMethod.hpp src/share/vm/oops/method.hpp src/share/vm/oops/methodCounters.cpp src/share/vm/oops/methodCounters.hpp src/share/vm/oops/methodData.cpp src/share/vm/oops/methodData.hpp src/share/vm/opto/compile.cpp src/share/vm/opto/compile.hpp src/share/vm/opto/parse.hpp src/share/vm/opto/parse1.cpp src/share/vm/runtime/deoptimization.cpp src/share/vm/runtime/deoptimization.hpp src/share/vm/runtime/globals.hpp src/share/vm/runtime/sweeper.cpp src/share/vm/runtime/sweeper.hpp src/share/vm/runtime/vmStructs.cpp
diffstat 27 files changed, 283 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/cpu/x86/vm/c1_CodeStubs_x86.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/cpu/x86/vm/c1_Runtime1_x86.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/c1/c1_CodeStubs.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/c1/c1_Compilation.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/c1/c1_LIRGenerator.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/c1/c1_LIRGenerator.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/c1/c1_Runtime1.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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(&reg_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/src/share/vm/c1/c1_Runtime1.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/ci/ciMethod.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/ci/ciMethod.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/oops/method.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/oops/methodCounters.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/oops/methodCounters.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/oops/methodData.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/oops/methodData.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/opto/compile.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/opto/compile.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/opto/parse.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/opto/parse1.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/deoptimization.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/deoptimization.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/globals.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/sweeper.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/sweeper.hpp	Tue May 13 11:25:17 2014 +0200
+++ b/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/src/share/vm/runtime/vmStructs.cpp	Tue May 13 11:25:17 2014 +0200
+++ b/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)                                    \