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

View file

@ -390,10 +390,10 @@ private:
u_char _saw_free_extra_data; u_char _saw_free_extra_data;
// Support for interprocedural escape analysis // Support for interprocedural escape analysis
intx _eflags; // flags on escape information intx _eflags; // flags on escape information
intx _arg_local; // bit set of non-escaping arguments intx _arg_local; // bit set of non-escaping arguments
intx _arg_stack; // bit set of stack-allocatable arguments intx _arg_stack; // bit set of stack-allocatable arguments
intx _arg_returned; // bit set of returned arguments intx _arg_returned; // bit set of returned arguments
// Maturity of the oop when the snapshot is taken. // Maturity of the oop when the snapshot is taken.
int _current_mileage; int _current_mileage;
@ -405,17 +405,15 @@ private:
int _backedge_counter; int _backedge_counter;
// Coherent snapshot of original header. // Coherent snapshot of original header.
MethodData _orig; MethodData::CompilerCounters _orig;
// Area dedicated to parameters. NULL if no parameter profiling for // Area dedicated to parameters. NULL if no parameter profiling for this method.
// this method.
DataLayout* _parameters; DataLayout* _parameters;
int parameters_size() const { int parameters_size() const {
return _parameters == NULL ? 0 : parameters_type_data()->size_in_bytes(); return _parameters == NULL ? 0 : parameters_type_data()->size_in_bytes();
} }
ciMethodData(MethodData* md); ciMethodData(MethodData* md = NULL);
ciMethodData();
// Accessors // Accessors
int data_size() const { return _data_size; } int data_size() const { return _data_size; }
@ -544,8 +542,8 @@ public:
uint trap_count(int reason) const { uint trap_count(int reason) const {
return _orig.trap_count(reason); return _orig.trap_count(reason);
} }
uint trap_reason_limit() const { return _orig.trap_reason_limit(); } uint trap_reason_limit() const { return MethodData::trap_reason_limit(); }
uint trap_count_limit() const { return _orig.trap_count_limit(); } uint trap_count_limit() const { return MethodData::trap_count_limit(); }
// Helpful query functions that decode trap_state. // Helpful query functions that decode trap_state.
int has_trap_at(ciProfileData* data, int reason); int has_trap_at(ciProfileData* data, int reason);

View file

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

View file

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

View file

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

View file

@ -31,6 +31,7 @@
#include "oops/oop.hpp" #include "oops/oop.hpp"
#include "runtime/atomic.hpp" #include "runtime/atomic.hpp"
#include "utilities/align.hpp" #include "utilities/align.hpp"
#include "utilities/copy.hpp"
class BytecodeStream; class BytecodeStream;
@ -1930,12 +1931,15 @@ class FailedSpeculation: public CHeapObj<mtCompiler> {
}; };
#endif #endif
class ciMethodData;
class MethodData : public Metadata { class MethodData : public Metadata {
friend class VMStructs; friend class VMStructs;
friend class JVMCIVMStructs; friend class JVMCIVMStructs;
private: private:
friend class ProfileData; friend class ProfileData;
friend class TypeEntriesAtCall; friend class TypeEntriesAtCall;
friend class ciMethodData;
// If you add a new field that points to any metaspace object, you // If you add a new field that points to any metaspace object, you
// must add this field to MethodData::metaspace_pointers_do(). // must add this field to MethodData::metaspace_pointers_do().
@ -1951,10 +1955,9 @@ private:
Mutex _extra_data_lock; Mutex _extra_data_lock;
MethodData(const methodHandle& method, int size, TRAPS); MethodData(const methodHandle& method);
public: public:
static MethodData* allocate(ClassLoaderData* loader_data, const methodHandle& method, TRAPS); 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; } virtual bool is_methodData() const { return true; }
void initialize(); void initialize();
@ -1965,14 +1968,75 @@ public:
_trap_hist_mask = max_jubyte, _trap_hist_mask = max_jubyte,
_extra_data_count = 4 // extra DataLayout headers, for trap history _extra_data_count = 4 // extra DataLayout headers, for trap history
}; // Public flag values }; // Public flag values
// 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 *) 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: private:
uint _nof_decompiles; // count of all nmethod removals CompilerCounters _compiler_counters;
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];
} _trap_hist;
// Support for interprocedural escape analysis, from Thomas Kotzmann. // Support for interprocedural escape analysis, from Thomas Kotzmann.
intx _eflags; // flags on escape information intx _eflags; // flags on escape information
@ -1980,8 +2044,6 @@ private:
intx _arg_stack; // bit set of stack-allocatable arguments intx _arg_stack; // bit set of stack-allocatable arguments
intx _arg_returned; // bit set of returned arguments intx _arg_returned; // bit set of returned arguments
int _creation_mileage; // method mileage at MDO creation
// How many invocations has this MDO seen? // How many invocations has this MDO seen?
// These counters are used to determine the exact age of MDO. // These counters are used to determine the exact age of MDO.
// We need those because in tiered a method can be concurrently // 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_in_bytes() const { return _size; }
int size() const { return align_metadata_size(align_up(_size, BytesPerWord)/BytesPerWord); } int size() const { return align_metadata_size(align_up(_size, BytesPerWord)/BytesPerWord); }
int creation_mileage() const { return _creation_mileage; } int creation_mileage() const { return _compiler_counters.creation_mileage(); }
void set_creation_mileage(int x) { _creation_mileage = x; }
int invocation_count() { int invocation_count() {
if (invocation_counter()->carry()) { if (invocation_counter()->carry()) {
@ -2302,42 +2363,33 @@ public:
// Return (uint)-1 for overflow. // Return (uint)-1 for overflow.
uint trap_count(int reason) const { uint trap_count(int reason) const {
assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob"); return _compiler_counters.trap_count(reason);
return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1;
} }
// For loops: // For loops:
static uint trap_reason_limit() { return _trap_hist_limit; } static uint trap_reason_limit() { return _trap_hist_limit; }
static uint trap_count_limit() { return _trap_hist_mask; } static uint trap_count_limit() { return _trap_hist_mask; }
uint inc_trap_count(int reason) { uint inc_trap_count(int reason) {
// Count another trap, anywhere in this method. return _compiler_counters.inc_trap_count(reason);
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 { uint overflow_trap_count() const {
return _nof_overflow_traps; return _compiler_counters.overflow_trap_count();
} }
uint overflow_recompile_count() const { uint overflow_recompile_count() const {
return _nof_overflow_recompiles; return _compiler_counters.overflow_recompile_count();
} }
void inc_overflow_recompile_count() { uint inc_overflow_recompile_count() {
_nof_overflow_recompiles += 1; return _compiler_counters.inc_overflow_recompile_count();
} }
uint decompile_count() const { uint decompile_count() const {
return _nof_decompiles; return _compiler_counters.decompile_count();
} }
void inc_decompile_count() { uint inc_decompile_count() {
_nof_decompiles += 1; uint dec_count = _compiler_counters.inc_decompile_count();
if (decompile_count() > (uint)PerMethodRecompilationCutoff) { if (dec_count > (uint)PerMethodRecompilationCutoff) {
method()->set_not_compilable("decompile_count > PerMethodRecompilationCutoff", CompLevel_full_optimization); method()->set_not_compilable("decompile_count > PerMethodRecompilationCutoff", CompLevel_full_optimization);
} }
return dec_count;
} }
uint tenure_traps() const { uint tenure_traps() const {
return _tenure_traps; return _tenure_traps;
@ -2363,7 +2415,7 @@ public:
} }
static ByteSize trap_history_offset() { 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() { 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_size, int) \
nonstatic_field(MethodData, _data[0], intptr_t) \ nonstatic_field(MethodData, _data[0], intptr_t) \
nonstatic_field(MethodData, _parameters_type_data_di, int) \ nonstatic_field(MethodData, _parameters_type_data_di, int) \
nonstatic_field(MethodData, _nof_decompiles, uint) \ nonstatic_field(MethodData, _compiler_counters._nof_decompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_recompiles, uint) \ nonstatic_field(MethodData, _compiler_counters._nof_overflow_recompiles, uint) \
nonstatic_field(MethodData, _nof_overflow_traps, uint) \ nonstatic_field(MethodData, _compiler_counters._nof_overflow_traps, uint) \
nonstatic_field(MethodData, _trap_hist._array[0], u1) \ nonstatic_field(MethodData, _compiler_counters._trap_hist._array[0], u1) \
nonstatic_field(MethodData, _eflags, intx) \ nonstatic_field(MethodData, _eflags, intx) \
nonstatic_field(MethodData, _arg_local, intx) \ nonstatic_field(MethodData, _arg_local, intx) \
nonstatic_field(MethodData, _arg_stack, 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_stack, intx) \
nonstatic_field(ciMethodData, _arg_returned, intx) \ nonstatic_field(ciMethodData, _arg_returned, intx) \
nonstatic_field(ciMethodData, _current_mileage, int) \ 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, _holder, ciInstanceKlass*) \
nonstatic_field(ciField, _name, ciSymbol*) \ nonstatic_field(ciField, _name, ciSymbol*) \
@ -1264,6 +1264,8 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
declare_type(MethodCounters, MetaspaceObj) \ declare_type(MethodCounters, MetaspaceObj) \
declare_type(ConstMethod, MetaspaceObj) \ declare_type(ConstMethod, MetaspaceObj) \
\ \
declare_toplevel_type(MethodData::CompilerCounters) \
\
declare_toplevel_type(narrowKlass) \ declare_toplevel_type(narrowKlass) \
\ \
declare_toplevel_type(vtableEntry) \ 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 methodDataSize = getFieldOffset("MethodData::_size", Integer.class, "int");
final int methodDataDataSize = getFieldOffset("MethodData::_data_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 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 methodDataIRSizeOffset = getFieldOffset("MethodData::_jvmci_ir_size", Integer.class, "int");
final int methodDataDecompiles = getFieldOffset("MethodData::_nof_decompiles", Integer.class, "uint"); final int methodDataDecompiles = getFieldOffset("MethodData::_compiler_counters._nof_decompiles", Integer.class, "uint");
final int methodDataOverflowRecompiles = getFieldOffset("MethodData::_nof_overflow_recompiles", Integer.class, "uint"); final int methodDataOverflowRecompiles = getFieldOffset("MethodData::_compiler_counters._nof_overflow_recompiles", Integer.class, "uint");
final int methodDataOverflowTraps = getFieldOffset("MethodData::_nof_overflow_traps", 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"); final int nmethodCompLevelOffset = getFieldOffset("nmethod::_comp_level", Integer.class, "int");