8032463: VirtualDispatch test timeout with DeoptimizeALot
Introduce code aging for warm method detection Reviewed-by: kvn, twisti
This commit is contained in:
parent
5938d3d5b9
commit
54db2c2d61
@ -414,6 +414,7 @@ void PatchingStub::emit_code(LIR_Assembler* ce) {
|
|||||||
|
|
||||||
void DeoptimizeStub::emit_code(LIR_Assembler* ce) {
|
void DeoptimizeStub::emit_code(LIR_Assembler* ce) {
|
||||||
__ bind(_entry);
|
__ bind(_entry);
|
||||||
|
__ set(_trap_request, G4);
|
||||||
__ call(Runtime1::entry_for(Runtime1::deoptimize_id), relocInfo::runtime_call_type);
|
__ call(Runtime1::entry_for(Runtime1::deoptimize_id), relocInfo::runtime_call_type);
|
||||||
__ delayed()->nop();
|
__ delayed()->nop();
|
||||||
ce->add_call_info_here(_info);
|
ce->add_call_info_here(_info);
|
||||||
|
@ -781,7 +781,7 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) {
|
|||||||
{
|
{
|
||||||
__ set_info("deoptimize", dont_gc_arguments);
|
__ set_info("deoptimize", dont_gc_arguments);
|
||||||
OopMap* oop_map = save_live_registers(sasm);
|
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 = new OopMapSet();
|
||||||
oop_maps->add_gc_map(call_offset, oop_map);
|
oop_maps->add_gc_map(call_offset, oop_map);
|
||||||
restore_live_registers(sasm);
|
restore_live_registers(sasm);
|
||||||
|
@ -430,6 +430,7 @@ void PatchingStub::emit_code(LIR_Assembler* ce) {
|
|||||||
|
|
||||||
void DeoptimizeStub::emit_code(LIR_Assembler* ce) {
|
void DeoptimizeStub::emit_code(LIR_Assembler* ce) {
|
||||||
__ bind(_entry);
|
__ bind(_entry);
|
||||||
|
ce->store_parameter(_trap_request, 0);
|
||||||
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::deoptimize_id)));
|
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::deoptimize_id)));
|
||||||
ce->add_call_info_here(_info);
|
ce->add_call_info_here(_info);
|
||||||
DEBUG_ONLY(__ should_not_reach_here());
|
DEBUG_ONLY(__ should_not_reach_here());
|
||||||
|
@ -1468,9 +1468,10 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) {
|
|||||||
case deoptimize_id:
|
case deoptimize_id:
|
||||||
{
|
{
|
||||||
StubFrame f(sasm, "deoptimize", dont_gc_arguments);
|
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);
|
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 = new OopMapSet();
|
||||||
oop_maps->add_gc_map(call_offset, oop_map);
|
oop_maps->add_gc_map(call_offset, oop_map);
|
||||||
restore_live_registers(sasm);
|
restore_live_registers(sasm);
|
||||||
|
@ -450,9 +450,11 @@ class PatchingStub: public CodeStub {
|
|||||||
class DeoptimizeStub : public CodeStub {
|
class DeoptimizeStub : public CodeStub {
|
||||||
private:
|
private:
|
||||||
CodeEmitInfo* _info;
|
CodeEmitInfo* _info;
|
||||||
|
jint _trap_request;
|
||||||
|
|
||||||
public:
|
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 void emit_code(LIR_Assembler* e);
|
||||||
virtual CodeEmitInfo* info() const { return _info; }
|
virtual CodeEmitInfo* info() const { return _info; }
|
||||||
|
@ -251,6 +251,10 @@ class Compilation: public StackObj {
|
|||||||
return env()->comp_level() == CompLevel_full_profile &&
|
return env()->comp_level() == CompLevel_full_profile &&
|
||||||
C1UpdateMethodData && MethodData::profile_return();
|
C1UpdateMethodData && MethodData::profile_return();
|
||||||
}
|
}
|
||||||
|
bool age_code() const {
|
||||||
|
return _method->profile_aging();
|
||||||
|
}
|
||||||
|
|
||||||
// will compilation make optimistic assumptions that might lead to
|
// will compilation make optimistic assumptions that might lead to
|
||||||
// deoptimization and that the runtime will account for?
|
// deoptimization and that the runtime will account for?
|
||||||
bool is_optimistic() const {
|
bool is_optimistic() const {
|
||||||
|
@ -2782,7 +2782,10 @@ void LIRGenerator::do_Base(Base* x) {
|
|||||||
__ lock_object(syncTempOpr(), obj, lock, new_register(T_OBJECT), slow_path, NULL);
|
__ 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
|
// increment invocation counters if needed
|
||||||
if (!method()->is_accessor()) { // Accessors do not have MDOs, so no counting.
|
if (!method()->is_accessor()) { // Accessors do not have MDOs, so no counting.
|
||||||
profile_parameters(x);
|
profile_parameters(x);
|
||||||
@ -3328,6 +3331,27 @@ void LIRGenerator::increment_event_counter(CodeEmitInfo* info, int bci, bool bac
|
|||||||
increment_event_counter_impl(info, info->scope()->method(), (1 << freq_log) - 1, bci, backedge, true);
|
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,
|
void LIRGenerator::increment_event_counter_impl(CodeEmitInfo* info,
|
||||||
ciMethod *method, int frequency,
|
ciMethod *method, int frequency,
|
||||||
int bci, bool backedge, bool notify) {
|
int bci, bool backedge, bool notify) {
|
||||||
|
@ -372,7 +372,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
|
|||||||
increment_event_counter(info, bci, true);
|
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, ValueStack* state, bool ignore_xhandler = false);
|
||||||
CodeEmitInfo* state_for(Instruction* x);
|
CodeEmitInfo* state_for(Instruction* x);
|
||||||
|
|
||||||
|
@ -685,19 +685,32 @@ JRT_LEAF(void, Runtime1::monitorexit(JavaThread* thread, BasicObjectLock* lock))
|
|||||||
JRT_END
|
JRT_END
|
||||||
|
|
||||||
// Cf. OptoRuntime::deoptimize_caller_frame
|
// 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
|
// Called from within the owner thread, so no need for safepoint
|
||||||
RegisterMap reg_map(thread, false);
|
RegisterMap reg_map(thread, false);
|
||||||
frame stub_frame = thread->last_frame();
|
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);
|
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.
|
if (action == Deoptimization::Action_make_not_entrant) {
|
||||||
assert(CodeCache::find_nmethod(caller_frame.pc()) != NULL, "sanity");
|
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.
|
// Deoptimize the caller frame.
|
||||||
Deoptimization::deoptimize_frame(thread, caller_frame.id());
|
Deoptimization::deoptimize_frame(thread, caller_frame.id());
|
||||||
|
|
||||||
// Return to the now deoptimized frame.
|
// Return to the now deoptimized frame.
|
||||||
JRT_END
|
JRT_END
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ class Runtime1: public AllStatic {
|
|||||||
static void monitorenter(JavaThread* thread, oopDesc* obj, BasicObjectLock* lock);
|
static void monitorenter(JavaThread* thread, oopDesc* obj, BasicObjectLock* lock);
|
||||||
static void monitorexit (JavaThread* thread, 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 access_field_patching(JavaThread* thread);
|
||||||
static int move_klass_patching(JavaThread* thread);
|
static int move_klass_patching(JavaThread* thread);
|
||||||
|
@ -129,6 +129,7 @@ ciMethod::ciMethod(methodHandle h_m) : ciMetadata(h_m()) {
|
|||||||
constantPoolHandle cpool = h_m()->constants();
|
constantPoolHandle cpool = h_m()->constants();
|
||||||
_signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
|
_signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
|
||||||
_method_data = NULL;
|
_method_data = NULL;
|
||||||
|
_nmethod_age = h_m()->nmethod_age();
|
||||||
// Take a snapshot of these values, so they will be commensurate with the MDO.
|
// Take a snapshot of these values, so they will be commensurate with the MDO.
|
||||||
if (ProfileInterpreter || TieredCompilation) {
|
if (ProfileInterpreter || TieredCompilation) {
|
||||||
int invcnt = h_m()->interpreter_invocation_count();
|
int invcnt = h_m()->interpreter_invocation_count();
|
||||||
@ -1275,6 +1276,14 @@ bool ciMethod::check_call(int refinfo_index, bool is_static) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// 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
|
// ciMethod::print_codes
|
||||||
//
|
//
|
||||||
|
@ -68,6 +68,7 @@ class ciMethod : public ciMetadata {
|
|||||||
int _max_locals;
|
int _max_locals;
|
||||||
vmIntrinsics::ID _intrinsic_id;
|
vmIntrinsics::ID _intrinsic_id;
|
||||||
int _handler_count;
|
int _handler_count;
|
||||||
|
int _nmethod_age;
|
||||||
int _interpreter_invocation_count;
|
int _interpreter_invocation_count;
|
||||||
int _interpreter_throwout_count;
|
int _interpreter_throwout_count;
|
||||||
int _instructions_size;
|
int _instructions_size;
|
||||||
@ -168,6 +169,10 @@ class ciMethod : public ciMetadata {
|
|||||||
int interpreter_invocation_count() const { check_is_loaded(); return _interpreter_invocation_count; }
|
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 interpreter_throwout_count() const { check_is_loaded(); return _interpreter_throwout_count; }
|
||||||
int size_of_parameters() const { check_is_loaded(); return _size_of_parameters; }
|
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.
|
// Code size for inlining decisions.
|
||||||
int code_size_for_inlining();
|
int code_size_for_inlining();
|
||||||
|
@ -371,6 +371,13 @@ class Method : public Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
int nmethod_age() const {
|
||||||
|
if (method_counters() == NULL) {
|
||||||
|
return INT_MAX;
|
||||||
|
} else {
|
||||||
|
return method_counters()->nmethod_age();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int invocation_count();
|
int invocation_count();
|
||||||
int backedge_count();
|
int backedge_count();
|
||||||
|
@ -34,4 +34,5 @@ void MethodCounters::clear_counters() {
|
|||||||
backedge_counter()->reset();
|
backedge_counter()->reset();
|
||||||
set_interpreter_throwout_count(0);
|
set_interpreter_throwout_count(0);
|
||||||
set_interpreter_invocation_count(0);
|
set_interpreter_invocation_count(0);
|
||||||
|
set_nmethod_age(INT_MAX);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,15 @@ class MethodCounters: public MetaspaceObj {
|
|||||||
u2 _number_of_breakpoints; // fullspeed debugging support
|
u2 _number_of_breakpoints; // fullspeed debugging support
|
||||||
InvocationCounter _invocation_counter; // Incremented before each activation of the method - used to trigger frequency-based optimizations
|
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
|
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
|
#ifdef TIERED
|
||||||
float _rate; // Events (invocation and backedge counter increments) per millisecond
|
float _rate; // Events (invocation and backedge counter increments) per millisecond
|
||||||
@ -44,7 +53,8 @@ class MethodCounters: public MetaspaceObj {
|
|||||||
|
|
||||||
MethodCounters() : _interpreter_invocation_count(0),
|
MethodCounters() : _interpreter_invocation_count(0),
|
||||||
_interpreter_throwout_count(0),
|
_interpreter_throwout_count(0),
|
||||||
_number_of_breakpoints(0)
|
_number_of_breakpoints(0),
|
||||||
|
_nmethod_age(INT_MAX)
|
||||||
#ifdef TIERED
|
#ifdef TIERED
|
||||||
, _rate(0),
|
, _rate(0),
|
||||||
_prev_time(0)
|
_prev_time(0)
|
||||||
@ -52,6 +62,10 @@ class MethodCounters: public MetaspaceObj {
|
|||||||
{
|
{
|
||||||
invocation_counter()->init();
|
invocation_counter()->init();
|
||||||
backedge_counter()->init();
|
backedge_counter()->init();
|
||||||
|
|
||||||
|
if (StressCodeAging) {
|
||||||
|
set_nmethod_age(HotMethodDetectionLimit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -104,6 +118,24 @@ class MethodCounters: public MetaspaceObj {
|
|||||||
InvocationCounter* invocation_counter() { return &_invocation_counter; }
|
InvocationCounter* invocation_counter() { return &_invocation_counter; }
|
||||||
InvocationCounter* backedge_counter() { return &_backedge_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() {
|
static ByteSize interpreter_invocation_counter_offset() {
|
||||||
return byte_offset_of(MethodCounters, _interpreter_invocation_count);
|
return byte_offset_of(MethodCounters, _interpreter_invocation_count);
|
||||||
}
|
}
|
||||||
|
@ -1130,6 +1130,7 @@ void MethodData::init() {
|
|||||||
_backedge_counter.init();
|
_backedge_counter.init();
|
||||||
_invocation_counter_start = 0;
|
_invocation_counter_start = 0;
|
||||||
_backedge_counter_start = 0;
|
_backedge_counter_start = 0;
|
||||||
|
_tenure_traps = 0;
|
||||||
_num_loops = 0;
|
_num_loops = 0;
|
||||||
_num_blocks = 0;
|
_num_blocks = 0;
|
||||||
_highest_comp_level = 0;
|
_highest_comp_level = 0;
|
||||||
|
@ -2059,6 +2059,7 @@ private:
|
|||||||
// Counter values at the time profiling started.
|
// Counter values at the time profiling started.
|
||||||
int _invocation_counter_start;
|
int _invocation_counter_start;
|
||||||
int _backedge_counter_start;
|
int _backedge_counter_start;
|
||||||
|
uint _tenure_traps;
|
||||||
|
|
||||||
#if INCLUDE_RTM_OPT
|
#if INCLUDE_RTM_OPT
|
||||||
// State of RTM code generation during compilation of the method
|
// State of RTM code generation during compilation of the method
|
||||||
@ -2398,6 +2399,12 @@ public:
|
|||||||
method()->set_not_compilable(CompLevel_full_optimization, true, "decompile_count > PerMethodRecompilationCutoff");
|
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
|
// Return pointer to area dedicated to parameters in MDO
|
||||||
ParametersTypeData* parameters_type_data() const {
|
ParametersTypeData* parameters_type_data() const {
|
||||||
|
@ -1089,6 +1089,7 @@ void Compile::Init(int aliaslevel) {
|
|||||||
set_do_scheduling(OptoScheduling);
|
set_do_scheduling(OptoScheduling);
|
||||||
set_do_count_invocations(false);
|
set_do_count_invocations(false);
|
||||||
set_do_method_data_update(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
|
set_rtm_state(NoRTM); // No RTM lock eliding by default
|
||||||
#if INCLUDE_RTM_OPT
|
#if INCLUDE_RTM_OPT
|
||||||
if (UseRTMLocking && has_method() && (method()->method_data_or_null() != NULL)) {
|
if (UseRTMLocking && has_method() && (method()->method_data_or_null() != NULL)) {
|
||||||
|
@ -311,6 +311,7 @@ class Compile : public Phase {
|
|||||||
bool _do_freq_based_layout; // True if we intend to do frequency based block layout
|
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_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 _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.
|
int _AliasLevel; // Locally-adjusted version of AliasLevel flag.
|
||||||
bool _print_assembly; // True if we should dump assembly code for this compilation
|
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
|
bool _print_inlining; // True if we should print inlining for this compilation
|
||||||
@ -584,7 +585,9 @@ class Compile : public Phase {
|
|||||||
void set_do_count_invocations(bool z){ _do_count_invocations = z; }
|
void set_do_count_invocations(bool z){ _do_count_invocations = z; }
|
||||||
bool do_method_data_update() const { return _do_method_data_update; }
|
bool do_method_data_update() const { return _do_method_data_update; }
|
||||||
void set_do_method_data_update(bool z) { _do_method_data_update = z; }
|
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; }
|
bool print_assembly() const { return _print_assembly; }
|
||||||
void set_print_assembly(bool z) { _print_assembly = z; }
|
void set_print_assembly(bool z) { _print_assembly = z; }
|
||||||
bool print_inlining() const { return _print_inlining; }
|
bool print_inlining() const { return _print_inlining; }
|
||||||
|
@ -581,6 +581,7 @@ class Parse : public GraphKit {
|
|||||||
void jump_switch_ranges(Node* a, SwitchRange* lo, SwitchRange* hi, int depth = 0);
|
void jump_switch_ranges(Node* a, SwitchRange* lo, SwitchRange* hi, int depth = 0);
|
||||||
bool create_jump_tables(Node* a, SwitchRange* lo, SwitchRange* hi);
|
bool create_jump_tables(Node* a, SwitchRange* lo, SwitchRange* hi);
|
||||||
|
|
||||||
|
void decrement_age();
|
||||||
// helper functions for methodData style profiling
|
// helper functions for methodData style profiling
|
||||||
void test_counter_against_threshold(Node* cnt, int limit);
|
void test_counter_against_threshold(Node* cnt, int limit);
|
||||||
void increment_and_test_invocation_counter(int limit);
|
void increment_and_test_invocation_counter(int limit);
|
||||||
|
@ -568,6 +568,9 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses, Pars
|
|||||||
} else {
|
} else {
|
||||||
set_map(entry_map);
|
set_map(entry_map);
|
||||||
do_method_entry();
|
do_method_entry();
|
||||||
|
if (depth() == 1 && C->age_code()) {
|
||||||
|
decrement_age();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (depth() == 1) {
|
if (depth() == 1) {
|
||||||
// Add check to deoptimize the nmethod if RTM state was changed
|
// Add check to deoptimize the nmethod if RTM state was changed
|
||||||
@ -2048,6 +2051,31 @@ void Parse::rtm_deopt() {
|
|||||||
#endif
|
#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---------------------------------
|
//------------------------------return_current---------------------------------
|
||||||
// Append current _map to _exit_return
|
// Append current _map to _exit_return
|
||||||
void Parse::return_current(Node* value) {
|
void Parse::return_current(Node* value) {
|
||||||
|
@ -744,6 +744,8 @@ int Deoptimization::deoptimize_dependents() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Deoptimization::DeoptAction Deoptimization::_unloaded_action
|
||||||
|
= Deoptimization::Action_reinterpret;
|
||||||
|
|
||||||
#ifdef COMPILER2
|
#ifdef COMPILER2
|
||||||
bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS) {
|
bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS) {
|
||||||
@ -1185,6 +1187,23 @@ JRT_LEAF(void, Deoptimization::popframe_preserve_args(JavaThread* thread, int by
|
|||||||
}
|
}
|
||||||
JRT_END
|
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)
|
#if defined(COMPILER2) || defined(SHARK)
|
||||||
void Deoptimization::load_class_by_index(constantPoolHandle constant_pool, int index, TRAPS) {
|
void Deoptimization::load_class_by_index(constantPoolHandle constant_pool, int index, TRAPS) {
|
||||||
@ -1285,7 +1304,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
|||||||
|
|
||||||
// Ensure that we can record deopt. history:
|
// Ensure that we can record deopt. history:
|
||||||
// Need MDO to record RTM code generation state.
|
// 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 =
|
MethodData* trap_mdo =
|
||||||
get_method_data(thread, trap_method, create_if_missing);
|
get_method_data(thread, trap_method, create_if_missing);
|
||||||
@ -1421,7 +1440,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
|||||||
//
|
//
|
||||||
// The other actions cause immediate removal of the present code.
|
// 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_entrant = false;
|
||||||
bool make_not_compilable = false;
|
bool make_not_compilable = false;
|
||||||
bool reprofile = false;
|
bool reprofile = false;
|
||||||
@ -1548,7 +1567,6 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
|||||||
if (make_not_entrant && maybe_prior_recompile && maybe_prior_trap) {
|
if (make_not_entrant && maybe_prior_recompile && maybe_prior_trap) {
|
||||||
reprofile = true;
|
reprofile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take requested actions on the method:
|
// Take requested actions on the method:
|
||||||
@ -1577,6 +1595,11 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
|||||||
trap_mdo->atomic_set_rtm_state(ProfileRTM);
|
trap_mdo->atomic_set_rtm_state(ProfileRTM);
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
if (inc_recompile_count) {
|
||||||
@ -1609,24 +1632,6 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
|||||||
}
|
}
|
||||||
JRT_END
|
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*
|
ProfileData*
|
||||||
Deoptimization::query_update_method_data(MethodData* trap_mdo,
|
Deoptimization::query_update_method_data(MethodData* trap_mdo,
|
||||||
int trap_bci,
|
int trap_bci,
|
||||||
@ -1813,8 +1818,6 @@ const char* Deoptimization::format_trap_state(char* buf, size_t buflen,
|
|||||||
|
|
||||||
|
|
||||||
//--------------------------------statics--------------------------------------
|
//--------------------------------statics--------------------------------------
|
||||||
Deoptimization::DeoptAction Deoptimization::_unloaded_action
|
|
||||||
= Deoptimization::Action_reinterpret;
|
|
||||||
const char* Deoptimization::_trap_reason_name[Reason_LIMIT] = {
|
const char* Deoptimization::_trap_reason_name[Reason_LIMIT] = {
|
||||||
// Note: Keep this in sync. with enum DeoptReason.
|
// Note: Keep this in sync. with enum DeoptReason.
|
||||||
"none",
|
"none",
|
||||||
|
@ -62,6 +62,7 @@ class Deoptimization : AllStatic {
|
|||||||
Reason_speculate_class_check, // saw unexpected object class from type speculation
|
Reason_speculate_class_check, // saw unexpected object class from type speculation
|
||||||
Reason_speculate_null_check, // saw unexpected null from type speculation
|
Reason_speculate_null_check, // saw unexpected null from type speculation
|
||||||
Reason_rtm_state_change, // rtm state change detected
|
Reason_rtm_state_change, // rtm state change detected
|
||||||
|
Reason_tenured, // age of the code has reached the limit
|
||||||
Reason_LIMIT,
|
Reason_LIMIT,
|
||||||
// Note: Keep this enum in sync. with _trap_reason_name.
|
// Note: Keep this enum in sync. with _trap_reason_name.
|
||||||
Reason_RECORDED_LIMIT = Reason_bimorphic // some are not recorded per bc
|
Reason_RECORDED_LIMIT = Reason_bimorphic // some are not recorded per bc
|
||||||
@ -357,8 +358,8 @@ class Deoptimization : AllStatic {
|
|||||||
// returning to a deoptimized caller
|
// returning to a deoptimized caller
|
||||||
static void popframe_preserve_args(JavaThread* thread, int bytes_to_save, void* start_address);
|
static void popframe_preserve_args(JavaThread* thread, int bytes_to_save, void* start_address);
|
||||||
|
|
||||||
private:
|
|
||||||
static MethodData* get_method_data(JavaThread* thread, methodHandle m, bool create_if_missing);
|
static MethodData* get_method_data(JavaThread* thread, methodHandle m, bool create_if_missing);
|
||||||
|
private:
|
||||||
// Update the mdo's count and per-BCI reason bits, returning previous state:
|
// Update the mdo's count and per-BCI reason bits, returning previous state:
|
||||||
static ProfileData* query_update_method_data(MethodData* trap_mdo,
|
static ProfileData* query_update_method_data(MethodData* trap_mdo,
|
||||||
int trap_bci,
|
int trap_bci,
|
||||||
|
@ -2561,6 +2561,20 @@ class CommandLineFlags {
|
|||||||
diagnostic(bool, PrintMethodFlushingStatistics, false, \
|
diagnostic(bool, PrintMethodFlushingStatistics, false, \
|
||||||
"print statistics about method flushing") \
|
"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, \
|
develop(bool, UseRelocIndex, false, \
|
||||||
"Use an index to speed random access to relocations") \
|
"Use an index to speed random access to relocations") \
|
||||||
\
|
\
|
||||||
|
@ -573,37 +573,7 @@ int NMethodSweeper::process_nmethod(nmethod *nm) {
|
|||||||
SWEEP(nm);
|
SWEEP(nm);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (UseCodeCacheFlushing) {
|
possibly_flush(nm);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clean-up all inline caches that point to zombie/non-reentrant methods
|
// Clean-up all inline caches that point to zombie/non-reentrant methods
|
||||||
MutexLocker cl(CompiledIC_lock);
|
MutexLocker cl(CompiledIC_lock);
|
||||||
nm->cleanup_inline_caches();
|
nm->cleanup_inline_caches();
|
||||||
@ -612,6 +582,88 @@ int NMethodSweeper::process_nmethod(nmethod *nm) {
|
|||||||
return freed_memory;
|
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
|
// Print out some state information about the current sweep and the
|
||||||
// state of the code cache if it's requested.
|
// state of the code cache if it's requested.
|
||||||
void NMethodSweeper::log_sweep(const char* msg, const char* format, ...) {
|
void NMethodSweeper::log_sweep(const char* msg, const char* format, ...) {
|
||||||
|
@ -111,6 +111,7 @@ class NMethodSweeper : public AllStatic {
|
|||||||
static int hotness_counter_reset_val();
|
static int hotness_counter_reset_val();
|
||||||
static void report_state_change(nmethod* nm);
|
static void report_state_change(nmethod* nm);
|
||||||
static void possibly_enable_sweeper();
|
static void possibly_enable_sweeper();
|
||||||
|
static void possibly_flush(nmethod* nm);
|
||||||
static void print(); // Printing/debugging
|
static void print(); // Printing/debugging
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -361,10 +361,12 @@ typedef TwoOopHashtable<Symbol*, mtClass> SymbolTwoOopHashtable;
|
|||||||
nonstatic_field(MethodData, _arg_local, intx) \
|
nonstatic_field(MethodData, _arg_local, intx) \
|
||||||
nonstatic_field(MethodData, _arg_stack, intx) \
|
nonstatic_field(MethodData, _arg_stack, intx) \
|
||||||
nonstatic_field(MethodData, _arg_returned, 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._tag, u1) \
|
||||||
nonstatic_field(DataLayout, _header._struct._flags, u1) \
|
nonstatic_field(DataLayout, _header._struct._flags, u1) \
|
||||||
nonstatic_field(DataLayout, _header._struct._bci, u2) \
|
nonstatic_field(DataLayout, _header._struct._bci, u2) \
|
||||||
nonstatic_field(DataLayout, _cells[0], intptr_t) \
|
nonstatic_field(DataLayout, _cells[0], intptr_t) \
|
||||||
|
nonstatic_field(MethodCounters, _nmethod_age, int) \
|
||||||
nonstatic_field(MethodCounters, _interpreter_invocation_count, int) \
|
nonstatic_field(MethodCounters, _interpreter_invocation_count, int) \
|
||||||
nonstatic_field(MethodCounters, _interpreter_throwout_count, u2) \
|
nonstatic_field(MethodCounters, _interpreter_throwout_count, u2) \
|
||||||
nonstatic_field(MethodCounters, _number_of_breakpoints, u2) \
|
nonstatic_field(MethodCounters, _number_of_breakpoints, u2) \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user