8252049: Native memory leak in ciMethodData ctor

Reviewed-by: kbarrett, coleenp
This commit is contained in:
Vladimir Ivanov 2020-12-08 17:02:09 +00:00
parent fab6158c19
commit 044616bd71
8 changed files with 132 additions and 110 deletions

View File

@ -37,46 +37,21 @@
// ------------------------------------------------------------------
// ciMethodData::ciMethodData
//
ciMethodData::ciMethodData(MethodData* md) : ciMetadata(md) {
assert(md != NULL, "no null method data");
Copy::zero_to_words((HeapWord*) &_orig, sizeof(_orig) / sizeof(HeapWord));
_data = NULL;
_data_size = 0;
_extra_data_size = 0;
_current_mileage = 0;
_invocation_counter = 0;
_backedge_counter = 0;
_state = empty_state;
_saw_free_extra_data = false;
ciMethodData::ciMethodData(MethodData* md)
: ciMetadata(md),
_data_size(0), _extra_data_size(0), _data(NULL),
// Set an initial hint. Don't use set_hint_di() because
// first_di() may be out of bounds if data_size is 0.
_hint_di = first_di();
_hint_di(first_di()),
_state(empty_state),
_saw_free_extra_data(false),
// Initialize the escape information (to "don't know.");
_eflags = _arg_local = _arg_stack = _arg_returned = 0;
_parameters = NULL;
}
// ------------------------------------------------------------------
// ciMethodData::ciMethodData
//
// No MethodData*.
ciMethodData::ciMethodData() : ciMetadata(NULL) {
Copy::zero_to_words((HeapWord*) &_orig, sizeof(_orig) / sizeof(HeapWord));
_data = NULL;
_data_size = 0;
_extra_data_size = 0;
_current_mileage = 0;
_invocation_counter = 0;
_backedge_counter = 0;
_state = empty_state;
_saw_free_extra_data = false;
// Set an initial hint. Don't use set_hint_di() because
// first_di() may be out of bounds if data_size is 0.
_hint_di = first_di();
// Initialize the escape information (to "don't know.");
_eflags = _arg_local = _arg_stack = _arg_returned = 0;
_parameters = NULL;
}
_eflags(0), _arg_local(0), _arg_stack(0), _arg_returned(0),
_current_mileage(0),
_invocation_counter(0),
_backedge_counter(0),
_orig(),
_parameters(NULL) {}
// Check for entries that reference an unloaded method
class PrepareExtraDataClosure : public CleanExtraDataClosure {
@ -226,7 +201,8 @@ void ciMethodData::load_data() {
// _extra_data_size = extra_data_limit - extra_data_base
// total_size = _data_size + _extra_data_size
// args_data_limit = data_base + total_size - parameter_data_size
Copy::disjoint_words_atomic((HeapWord*) mdo,
static_assert(sizeof(_orig) % HeapWordSize == 0, "align");
Copy::disjoint_words_atomic((HeapWord*) &mdo->_compiler_counters,
(HeapWord*) &_orig,
sizeof(_orig) / HeapWordSize);
Arena* arena = CURRENT_ENV->arena();

View File

@ -405,17 +405,15 @@ private:
int _backedge_counter;
// Coherent snapshot of original header.
MethodData _orig;
MethodData::CompilerCounters _orig;
// Area dedicated to parameters. NULL if no parameter profiling for
// this method.
// Area dedicated to parameters. NULL if no parameter profiling for this method.
DataLayout* _parameters;
int parameters_size() const {
return _parameters == NULL ? 0 : parameters_type_data()->size_in_bytes();
}
ciMethodData(MethodData* md);
ciMethodData();
ciMethodData(MethodData* md = NULL);
// Accessors
int data_size() const { return _data_size; }
@ -544,8 +542,8 @@ public:
uint trap_count(int reason) const {
return _orig.trap_count(reason);
}
uint trap_reason_limit() const { return _orig.trap_reason_limit(); }
uint trap_count_limit() const { return _orig.trap_count_limit(); }
uint trap_reason_limit() const { return MethodData::trap_reason_limit(); }
uint trap_count_limit() const { return MethodData::trap_count_limit(); }
// Helpful query functions that decode trap_state.
int has_trap_at(ciProfileData* data, int reason);

View File

@ -286,7 +286,7 @@ class CompileReplay : public StackObj {
return NULL;
}
int actual_size = sizeof(MethodData);
int actual_size = sizeof(MethodData::CompilerCounters);
char *result = NEW_RESOURCE_ARRAY(char, actual_size);
int i = 0;
if (read_size != actual_size) {

View File

@ -237,10 +237,10 @@
nonstatic_field(MethodData, _data_size, int) \
nonstatic_field(MethodData, _data[0], intptr_t) \
nonstatic_field(MethodData, _parameters_type_data_di, int) \
nonstatic_field(MethodData, _nof_decompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_recompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_traps, uint) \
nonstatic_field(MethodData, _trap_hist._array[0], u1) \
nonstatic_field(MethodData, _compiler_counters._nof_decompiles, uint) \
nonstatic_field(MethodData, _compiler_counters._nof_overflow_recompiles, uint) \
nonstatic_field(MethodData, _compiler_counters._nof_overflow_traps, uint) \
nonstatic_field(MethodData, _compiler_counters._trap_hist._array[0], u1) \
nonstatic_field(MethodData, _eflags, intx) \
nonstatic_field(MethodData, _arg_local, intx) \
nonstatic_field(MethodData, _arg_stack, intx) \

View File

@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
#include "ci/ciMethodData.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compilerOracle.hpp"
@ -656,7 +657,7 @@ MethodData* MethodData::allocate(ClassLoaderData* loader_data, const methodHandl
int size = MethodData::compute_allocation_size_in_words(method);
return new (loader_data, size, MetaspaceObj::MethodDataType, THREAD)
MethodData(method, size, THREAD);
MethodData(method);
}
int MethodData::bytecode_cell_count(Bytecodes::Code code) {
@ -1202,11 +1203,11 @@ void MethodData::post_initialize(BytecodeStream* stream) {
}
// Initialize the MethodData* corresponding to a given method.
MethodData::MethodData(const methodHandle& method, int size, TRAPS)
: _extra_data_lock(Mutex::leaf, "MDO extra data lock"),
MethodData::MethodData(const methodHandle& method)
: _method(method()),
_extra_data_lock(Mutex::leaf, "MDO extra data lock"),
_compiler_counters(method()),
_parameters_type_data_di(parameters_uninitialized) {
// Set the method back-pointer.
_method = method();
initialize();
}
@ -1216,7 +1217,6 @@ void MethodData::initialize() {
ResourceMark rm(thread);
init();
set_creation_mileage(mileage_of(method()));
// Go through the bytecodes and allocate and initialize the
// corresponding data cells.
@ -1318,14 +1318,8 @@ void MethodData::init() {
}
#endif
// Initialize flags and trap history.
_nof_decompiles = 0;
_nof_overflow_recompiles = 0;
_nof_overflow_traps = 0;
// Initialize escape flags.
clear_escape_info();
assert(sizeof(_trap_hist) % sizeof(HeapWord) == 0, "align");
Copy::zero_to_words((HeapWord*) &_trap_hist,
sizeof(_trap_hist) / sizeof(HeapWord));
}
// Get a measure of how much mileage the method has on it.

View File

@ -31,6 +31,7 @@
#include "oops/oop.hpp"
#include "runtime/atomic.hpp"
#include "utilities/align.hpp"
#include "utilities/copy.hpp"
class BytecodeStream;
@ -1930,12 +1931,15 @@ class FailedSpeculation: public CHeapObj<mtCompiler> {
};
#endif
class ciMethodData;
class MethodData : public Metadata {
friend class VMStructs;
friend class JVMCIVMStructs;
private:
friend class ProfileData;
friend class TypeEntriesAtCall;
friend class ciMethodData;
// If you add a new field that points to any metaspace object, you
// must add this field to MethodData::metaspace_pointers_do().
@ -1951,10 +1955,9 @@ private:
Mutex _extra_data_lock;
MethodData(const methodHandle& method, int size, TRAPS);
MethodData(const methodHandle& method);
public:
static MethodData* allocate(ClassLoaderData* loader_data, const methodHandle& method, TRAPS);
MethodData() : _extra_data_lock(Mutex::leaf, "MDO extra data lock") {}; // For ciMethodData
virtual bool is_methodData() const { return true; }
void initialize();
@ -1965,23 +1968,82 @@ public:
_trap_hist_mask = max_jubyte,
_extra_data_count = 4 // extra DataLayout headers, for trap history
}; // Public flag values
private:
// Compiler-related counters.
class CompilerCounters {
friend class VMStructs;
friend class JVMCIVMStructs;
int _creation_mileage; // method mileage at MDO creation
uint _nof_decompiles; // count of all nmethod removals
uint _nof_overflow_recompiles; // recompile count, excluding recomp. bits
uint _nof_overflow_traps; // trap count, excluding _trap_hist
union {
intptr_t _align;
u1 _array[JVMCI_ONLY(2 *) _trap_hist_limit];
u1 _array[JVMCI_ONLY(2 *) MethodData::_trap_hist_limit];
} _trap_hist;
CompilerCounters(int current_mileage) : _creation_mileage(current_mileage), _nof_decompiles(0), _nof_overflow_recompiles(0), _nof_overflow_traps(0) {
static_assert(sizeof(_trap_hist) % HeapWordSize == 0, "align");
uint size_in_words = sizeof(_trap_hist) / HeapWordSize;
Copy::zero_to_words((HeapWord*) &_trap_hist, size_in_words);
}
public:
CompilerCounters(Method* m) : CompilerCounters(MethodData::mileage_of(m)) {}
CompilerCounters() : CompilerCounters(0) {} // for ciMethodData
int creation_mileage() const { return _creation_mileage; }
// Return (uint)-1 for overflow.
uint trap_count(int reason) const {
assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob");
return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1;
}
uint inc_trap_count(int reason) {
// Count another trap, anywhere in this method.
assert(reason >= 0, "must be single trap");
assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob");
uint cnt1 = 1 + _trap_hist._array[reason];
if ((cnt1 & _trap_hist_mask) != 0) { // if no counter overflow...
_trap_hist._array[reason] = cnt1;
return cnt1;
} else {
return _trap_hist_mask + (++_nof_overflow_traps);
}
}
uint overflow_trap_count() const {
return _nof_overflow_traps;
}
uint overflow_recompile_count() const {
return _nof_overflow_recompiles;
}
uint inc_overflow_recompile_count() {
return ++_nof_overflow_recompiles;
}
uint decompile_count() const {
return _nof_decompiles;
}
uint inc_decompile_count() {
return ++_nof_decompiles;
}
// Support for code generation
static ByteSize trap_history_offset() {
return byte_offset_of(CompilerCounters, _trap_hist._array);
}
};
private:
CompilerCounters _compiler_counters;
// Support for interprocedural escape analysis, from Thomas Kotzmann.
intx _eflags; // flags on escape information
intx _arg_local; // bit set of non-escaping arguments
intx _arg_stack; // bit set of stack-allocatable arguments
intx _arg_returned; // bit set of returned arguments
int _creation_mileage; // method mileage at MDO creation
// How many invocations has this MDO seen?
// These counters are used to determine the exact age of MDO.
// We need those because in tiered a method can be concurrently
@ -2126,8 +2188,7 @@ public:
int size_in_bytes() const { return _size; }
int size() const { return align_metadata_size(align_up(_size, BytesPerWord)/BytesPerWord); }
int creation_mileage() const { return _creation_mileage; }
void set_creation_mileage(int x) { _creation_mileage = x; }
int creation_mileage() const { return _compiler_counters.creation_mileage(); }
int invocation_count() {
if (invocation_counter()->carry()) {
@ -2302,42 +2363,33 @@ public:
// Return (uint)-1 for overflow.
uint trap_count(int reason) const {
assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob");
return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1;
return _compiler_counters.trap_count(reason);
}
// For loops:
static uint trap_reason_limit() { return _trap_hist_limit; }
static uint trap_count_limit() { return _trap_hist_mask; }
uint inc_trap_count(int reason) {
// Count another trap, anywhere in this method.
assert(reason >= 0, "must be single trap");
assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob");
uint cnt1 = 1 + _trap_hist._array[reason];
if ((cnt1 & _trap_hist_mask) != 0) { // if no counter overflow...
_trap_hist._array[reason] = cnt1;
return cnt1;
} else {
return _trap_hist_mask + (++_nof_overflow_traps);
}
return _compiler_counters.inc_trap_count(reason);
}
uint overflow_trap_count() const {
return _nof_overflow_traps;
return _compiler_counters.overflow_trap_count();
}
uint overflow_recompile_count() const {
return _nof_overflow_recompiles;
return _compiler_counters.overflow_recompile_count();
}
void inc_overflow_recompile_count() {
_nof_overflow_recompiles += 1;
uint inc_overflow_recompile_count() {
return _compiler_counters.inc_overflow_recompile_count();
}
uint decompile_count() const {
return _nof_decompiles;
return _compiler_counters.decompile_count();
}
void inc_decompile_count() {
_nof_decompiles += 1;
if (decompile_count() > (uint)PerMethodRecompilationCutoff) {
uint inc_decompile_count() {
uint dec_count = _compiler_counters.inc_decompile_count();
if (dec_count > (uint)PerMethodRecompilationCutoff) {
method()->set_not_compilable("decompile_count > PerMethodRecompilationCutoff", CompLevel_full_optimization);
}
return dec_count;
}
uint tenure_traps() const {
return _tenure_traps;
@ -2363,7 +2415,7 @@ public:
}
static ByteSize trap_history_offset() {
return byte_offset_of(MethodData, _trap_hist._array);
return byte_offset_of(MethodData, _compiler_counters) + CompilerCounters::trap_history_offset();
}
static ByteSize invocation_counter_offset() {

View File

@ -270,10 +270,10 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
nonstatic_field(MethodData, _data_size, int) \
nonstatic_field(MethodData, _data[0], intptr_t) \
nonstatic_field(MethodData, _parameters_type_data_di, int) \
nonstatic_field(MethodData, _nof_decompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_recompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_traps, uint) \
nonstatic_field(MethodData, _trap_hist._array[0], u1) \
nonstatic_field(MethodData, _compiler_counters._nof_decompiles, uint) \
nonstatic_field(MethodData, _compiler_counters._nof_overflow_recompiles, uint) \
nonstatic_field(MethodData, _compiler_counters._nof_overflow_traps, uint) \
nonstatic_field(MethodData, _compiler_counters._trap_hist._array[0], u1) \
nonstatic_field(MethodData, _eflags, intx) \
nonstatic_field(MethodData, _arg_local, intx) \
nonstatic_field(MethodData, _arg_stack, intx) \
@ -856,7 +856,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
nonstatic_field(ciMethodData, _arg_stack, intx) \
nonstatic_field(ciMethodData, _arg_returned, intx) \
nonstatic_field(ciMethodData, _current_mileage, int) \
nonstatic_field(ciMethodData, _orig, MethodData) \
nonstatic_field(ciMethodData, _orig, MethodData::CompilerCounters) \
\
nonstatic_field(ciField, _holder, ciInstanceKlass*) \
nonstatic_field(ciField, _name, ciSymbol*) \
@ -1264,6 +1264,8 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
declare_type(MethodCounters, MetaspaceObj) \
declare_type(ConstMethod, MetaspaceObj) \
\
declare_toplevel_type(MethodData::CompilerCounters) \
\
declare_toplevel_type(narrowKlass) \
\
declare_toplevel_type(vtableEntry) \

View File

@ -177,12 +177,12 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
final int methodDataSize = getFieldOffset("MethodData::_size", Integer.class, "int");
final int methodDataDataSize = getFieldOffset("MethodData::_data_size", Integer.class, "int");
final int methodDataOopDataOffset = getFieldOffset("MethodData::_data[0]", Integer.class, "intptr_t");
final int methodDataOopTrapHistoryOffset = getFieldOffset("MethodData::_trap_hist._array[0]", Integer.class, "u1");
final int methodDataOopTrapHistoryOffset = getFieldOffset("MethodData::_compiler_counters._trap_hist._array[0]", Integer.class, "u1");
final int methodDataIRSizeOffset = getFieldOffset("MethodData::_jvmci_ir_size", Integer.class, "int");
final int methodDataDecompiles = getFieldOffset("MethodData::_nof_decompiles", Integer.class, "uint");
final int methodDataOverflowRecompiles = getFieldOffset("MethodData::_nof_overflow_recompiles", Integer.class, "uint");
final int methodDataOverflowTraps = getFieldOffset("MethodData::_nof_overflow_traps", Integer.class, "uint");
final int methodDataDecompiles = getFieldOffset("MethodData::_compiler_counters._nof_decompiles", Integer.class, "uint");
final int methodDataOverflowRecompiles = getFieldOffset("MethodData::_compiler_counters._nof_overflow_recompiles", Integer.class, "uint");
final int methodDataOverflowTraps = getFieldOffset("MethodData::_compiler_counters._nof_overflow_traps", Integer.class, "uint");
final int nmethodCompLevelOffset = getFieldOffset("nmethod::_comp_level", Integer.class, "int");