mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
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
27 changed files with 283 additions and 69 deletions
|
@ -573,37 +573,7 @@ int NMethodSweeper::process_nmethod(nmethod *nm) {
|
|||
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 @@ int NMethodSweeper::process_nmethod(nmethod *nm) {
|
|||
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, ...) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue