8317683: Add JIT memory statistics
Reviewed-by: kvn, adinn
This commit is contained in:
parent
f7d6d7a04f
commit
56aa1e8dc8
@ -33,7 +33,10 @@
|
||||
#include "c1/c1_ValueMap.hpp"
|
||||
#include "c1/c1_ValueStack.hpp"
|
||||
#include "code/debugInfoRec.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compilerDirectives.hpp"
|
||||
#include "compiler/compileLog.hpp"
|
||||
#include "compiler/compileTask.hpp"
|
||||
#include "compiler/compilerDirectives.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
@ -442,6 +445,9 @@ void Compilation::install_code(int frame_size) {
|
||||
|
||||
|
||||
void Compilation::compile_method() {
|
||||
|
||||
CompilationMemoryStatisticMark cmsm(env()->task()->directive());
|
||||
|
||||
{
|
||||
PhaseTraceTime timeit(_t_setup);
|
||||
|
||||
|
@ -219,7 +219,7 @@ void SymbolTable::create_table () {
|
||||
if (symbol_alloc_arena_size == 0) {
|
||||
_arena = new (mtSymbol) Arena(mtSymbol);
|
||||
} else {
|
||||
_arena = new (mtSymbol) Arena(mtSymbol, symbol_alloc_arena_size);
|
||||
_arena = new (mtSymbol) Arena(mtSymbol, Arena::Tag::tag_other, symbol_alloc_arena_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
451
src/hotspot/share/compiler/compilationMemoryStatistic.cpp
Normal file
451
src/hotspot/share/compiler/compilationMemoryStatistic.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "compiler/abstractCompiler.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compilerDirectives.hpp"
|
||||
#include "compiler/compileTask.hpp"
|
||||
#include "compiler/compilerDefinitions.hpp"
|
||||
#include "compiler/compilerThread.hpp"
|
||||
#include "memory/arena.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#ifdef COMPILER2
|
||||
#include "opto/node.hpp" // compile.hpp is not self-contained
|
||||
#include "opto/compile.hpp"
|
||||
#endif
|
||||
#include "services/nmtCommon.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/quickSort.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
|
||||
|
||||
ArenaStatCounter::ArenaStatCounter() :
|
||||
_current(0), _start(0), _peak(0),
|
||||
_na(0), _ra(0),
|
||||
_na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0)
|
||||
{}
|
||||
|
||||
size_t ArenaStatCounter::peak_since_start() const {
|
||||
return _peak > _start ? _peak - _start : 0;
|
||||
}
|
||||
|
||||
void ArenaStatCounter::start() {
|
||||
_peak = _start = _current;
|
||||
}
|
||||
|
||||
void ArenaStatCounter::update_c2_node_count() {
|
||||
#ifdef COMPILER2
|
||||
CompilerThread* const th = Thread::current()->as_Compiler_thread();
|
||||
const CompileTask* const task = th->task();
|
||||
if (task != nullptr &&
|
||||
th->task()->compiler() != nullptr &&
|
||||
th->task()->compiler()->type() == compiler_c2) {
|
||||
const Compile* const comp = Compile::current();
|
||||
if (comp != nullptr) {
|
||||
_live_nodes_at_peak = comp->live_nodes();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Account an arena allocation or de-allocation.
|
||||
bool ArenaStatCounter::account(ssize_t delta, int tag) {
|
||||
bool rc = false;
|
||||
#ifdef ASSERT
|
||||
// Note: if this fires, we free more arena memory under the scope of the
|
||||
// CompilationMemoryHistoryMark than we allocate. This cannot be since we
|
||||
// assume arena allocations in CompilerThread to be stack bound and symmetric.
|
||||
assert(delta >= 0 || ((ssize_t)_current + delta) >= 0,
|
||||
"Negative overflow (d=%zd %zu %zu %zu)", delta, _current, _start, _peak);
|
||||
#endif
|
||||
// Update totals
|
||||
_current += delta;
|
||||
// Update detail counter
|
||||
switch ((Arena::Tag)tag) {
|
||||
case Arena::Tag::tag_ra: _ra += delta; break;
|
||||
case Arena::Tag::tag_node: _na += delta; break;
|
||||
default: // ignore
|
||||
break;
|
||||
};
|
||||
// Did we reach a peak?
|
||||
if (_current > _peak) {
|
||||
_peak = _current;
|
||||
assert(delta > 0, "Sanity (%zu %zu %zu)", _current, _start, _peak);
|
||||
_na_at_peak = _na;
|
||||
_ra_at_peak = _ra;
|
||||
update_c2_node_count();
|
||||
rc = true;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ArenaStatCounter::print_on(outputStream* st) const {
|
||||
st->print("%zu [na %zu ra %zu]", peak_since_start(), _na_at_peak, _ra_at_peak);
|
||||
#ifdef ASSERT
|
||||
st->print(" (%zu->%zu->%zu)", _start, _peak, _current);
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// Backend
|
||||
|
||||
class FullMethodName {
|
||||
Symbol* const _k;
|
||||
Symbol* const _m;
|
||||
Symbol* const _s;
|
||||
|
||||
public:
|
||||
|
||||
FullMethodName(Symbol* k, Symbol* m, Symbol* s) : _k(k), _m(m), _s(s) {}
|
||||
FullMethodName(const FullMethodName& o) : _k(o._k), _m(o._m), _s(o._s) {}
|
||||
|
||||
void make_permanent() {
|
||||
_k->make_permanent();
|
||||
_m->make_permanent();
|
||||
_s->make_permanent();
|
||||
}
|
||||
|
||||
static unsigned compute_hash(const FullMethodName& n) {
|
||||
return Symbol::compute_hash(n._k) ^
|
||||
Symbol::compute_hash(n._m) ^
|
||||
Symbol::compute_hash(n._s);
|
||||
}
|
||||
|
||||
char* as_C_string(char* buf, size_t len) const {
|
||||
stringStream ss(buf, len);
|
||||
ResourceMark rm;
|
||||
ss.print_raw(_k->as_C_string());
|
||||
ss.print_raw("::");
|
||||
ss.print_raw(_m->as_C_string());
|
||||
ss.put('(');
|
||||
ss.print_raw(_s->as_C_string());
|
||||
ss.put(')');
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool equals(const FullMethodName& b) const {
|
||||
return _k == b._k && _m == b._m && _s == b._s;
|
||||
}
|
||||
|
||||
bool operator== (const FullMethodName& other) const { return equals(other); }
|
||||
};
|
||||
|
||||
// Note: not mtCompiler since we don't want to change what we measure
|
||||
class MemStatEntry : public CHeapObj<mtInternal> {
|
||||
const FullMethodName _method;
|
||||
CompilerType _comptype;
|
||||
double _time;
|
||||
// How often this has been recompiled.
|
||||
int _num_recomp;
|
||||
// Compiling thread. Only for diagnostic purposes. Thread may not be alive anymore.
|
||||
const Thread* _thread;
|
||||
|
||||
size_t _total;
|
||||
size_t _na_at_peak;
|
||||
size_t _ra_at_peak;
|
||||
unsigned _live_nodes_at_peak;
|
||||
|
||||
public:
|
||||
|
||||
MemStatEntry(FullMethodName method)
|
||||
: _method(method), _comptype(compiler_c1),
|
||||
_time(0), _num_recomp(0), _thread(nullptr),
|
||||
_total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) {
|
||||
}
|
||||
|
||||
void set_comptype(CompilerType comptype) { _comptype = comptype; }
|
||||
void set_current_time() { _time = os::elapsedTime(); }
|
||||
void set_current_thread() { _thread = Thread::current(); }
|
||||
void inc_recompilation() { _num_recomp++; }
|
||||
|
||||
void set_total(size_t n) { _total = n; }
|
||||
void set_na_at_peak(size_t n) { _na_at_peak = n; }
|
||||
void set_ra_at_peak(size_t n) { _ra_at_peak = n; }
|
||||
void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; }
|
||||
|
||||
size_t total() const { return _total; }
|
||||
|
||||
static void print_legend(outputStream* st) {
|
||||
st->print_cr("Legend:");
|
||||
st->print_cr(" total : memory allocated via arenas while compiling");
|
||||
st->print_cr(" NA : ...how much in node arenas (if c2)");
|
||||
st->print_cr(" RA : ...how much in resource areas");
|
||||
st->print_cr(" #nodes : ...how many nodes (if c2)");
|
||||
st->print_cr(" time : time of last compilation (sec)");
|
||||
st->print_cr(" type : compiler type");
|
||||
st->print_cr(" #rc : how often recompiled");
|
||||
st->print_cr(" thread : compiler thread");
|
||||
}
|
||||
|
||||
static void print_header(outputStream* st) {
|
||||
st->print_cr("total NA RA #nodes time type #rc thread method");
|
||||
}
|
||||
|
||||
void print_on(outputStream* st, bool human_readable) const {
|
||||
int col = 0;
|
||||
|
||||
// Total
|
||||
if (human_readable) {
|
||||
st->print(PROPERFMT " ", PROPERFMTARGS(_total));
|
||||
} else {
|
||||
st->print("%zu ", _total);
|
||||
}
|
||||
col += 10; st->fill_to(col);
|
||||
|
||||
// NA
|
||||
if (human_readable) {
|
||||
st->print(PROPERFMT " ", PROPERFMTARGS(_na_at_peak));
|
||||
} else {
|
||||
st->print("%zu ", _na_at_peak);
|
||||
}
|
||||
col += 10; st->fill_to(col);
|
||||
|
||||
// RA
|
||||
if (human_readable) {
|
||||
st->print(PROPERFMT " ", PROPERFMTARGS(_ra_at_peak));
|
||||
} else {
|
||||
st->print("%zu ", _ra_at_peak);
|
||||
}
|
||||
col += 10; st->fill_to(col);
|
||||
|
||||
// Number of Nodes when memory peaked
|
||||
st->print("%u ", _live_nodes_at_peak);
|
||||
col += 8; st->fill_to(col);
|
||||
|
||||
// TimeStamp
|
||||
st->print("%.3f ", _time);
|
||||
col += 8; st->fill_to(col);
|
||||
|
||||
// Type
|
||||
st->print("%s ", compilertype2name(_comptype));
|
||||
col += 6; st->fill_to(col);
|
||||
|
||||
// Recomp
|
||||
st->print("%u ", _num_recomp);
|
||||
col += 4; st->fill_to(col);
|
||||
|
||||
// Thread
|
||||
st->print(PTR_FORMAT " ", p2i(_thread));
|
||||
|
||||
// MethodName
|
||||
char buf[1024];
|
||||
st->print("%s ", _method.as_C_string(buf, sizeof(buf)));
|
||||
st->cr();
|
||||
}
|
||||
|
||||
int compare_by_size(const MemStatEntry* b) const {
|
||||
const size_t x1 = b->_total;
|
||||
const size_t x2 = _total;
|
||||
return x1 < x2 ? -1 : x1 == x2 ? 0 : 1;
|
||||
}
|
||||
|
||||
bool equals(const FullMethodName& b) const {
|
||||
return _method.equals(b);
|
||||
}
|
||||
};
|
||||
|
||||
class MemStatTable :
|
||||
public ResourceHashtable<FullMethodName, MemStatEntry*, 7919, AnyObj::C_HEAP,
|
||||
mtInternal, FullMethodName::compute_hash>
|
||||
{
|
||||
public:
|
||||
|
||||
void add(const FullMethodName& fmn, CompilerType comptype,
|
||||
size_t total, size_t na_at_peak, size_t ra_at_peak,
|
||||
unsigned live_nodes_at_peak) {
|
||||
assert_lock_strong(NMTCompilationCostHistory_lock);
|
||||
|
||||
MemStatEntry** pe = get(fmn);
|
||||
MemStatEntry* e = nullptr;
|
||||
if (pe == nullptr) {
|
||||
e = new MemStatEntry(fmn);
|
||||
put(fmn, e);
|
||||
} else {
|
||||
// Update existing entry
|
||||
e = *pe;
|
||||
assert(e != nullptr, "Sanity");
|
||||
}
|
||||
e->set_current_time();
|
||||
e->set_current_thread();
|
||||
e->set_comptype(comptype);
|
||||
e->inc_recompilation();
|
||||
e->set_total(total);
|
||||
e->set_na_at_peak(na_at_peak);
|
||||
e->set_ra_at_peak(ra_at_peak);
|
||||
e->set_live_nodes_at_peak(live_nodes_at_peak);
|
||||
}
|
||||
|
||||
// Returns a C-heap-allocated SortMe array containing all entries from the table,
|
||||
// optionally filtered by entry size
|
||||
MemStatEntry** calc_flat_array(int& num, size_t min_size) {
|
||||
assert_lock_strong(NMTCompilationCostHistory_lock);
|
||||
|
||||
const int num_all = number_of_entries();
|
||||
MemStatEntry** flat = NEW_C_HEAP_ARRAY(MemStatEntry*, num_all, mtInternal);
|
||||
int i = 0;
|
||||
auto do_f = [&] (const FullMethodName& ignored, MemStatEntry* e) {
|
||||
if (e->total() >= min_size) {
|
||||
flat[i] = e;
|
||||
assert(i < num_all, "Sanity");
|
||||
i ++;
|
||||
}
|
||||
};
|
||||
iterate_all(do_f);
|
||||
if (min_size == 0) {
|
||||
assert(i == num_all, "Sanity");
|
||||
} else {
|
||||
assert(i <= num_all, "Sanity");
|
||||
}
|
||||
num = i;
|
||||
return flat;
|
||||
}
|
||||
};
|
||||
|
||||
bool CompilationMemoryStatistic::_enabled = false;
|
||||
|
||||
static MemStatTable* _the_table = nullptr;
|
||||
|
||||
void CompilationMemoryStatistic::initialize() {
|
||||
assert(_enabled == false && _the_table == nullptr, "Only once");
|
||||
_the_table = new (mtCompiler) MemStatTable;
|
||||
_enabled = true;
|
||||
log_info(compilation, alloc)("Compilation memory statistic enabled");
|
||||
}
|
||||
|
||||
void CompilationMemoryStatistic::on_start_compilation() {
|
||||
assert(enabled(), "Not enabled?");
|
||||
Thread::current()->as_Compiler_thread()->arena_stat()->start();
|
||||
}
|
||||
|
||||
void CompilationMemoryStatistic::on_end_compilation() {
|
||||
assert(enabled(), "Not enabled?");
|
||||
ResourceMark rm;
|
||||
CompilerThread* const th = Thread::current()->as_Compiler_thread();
|
||||
const ArenaStatCounter* const arena_stat = th->arena_stat();
|
||||
const CompilerType ct = th->task()->compiler()->type();
|
||||
|
||||
const Method* const m = th->task()->method();
|
||||
FullMethodName fmn(m->klass_name(), m->name(), m->signature());
|
||||
fmn.make_permanent();
|
||||
|
||||
const DirectiveSet* directive = th->task()->directive();
|
||||
assert(directive->should_collect_memstat(), "Only call if memstat is enabled");
|
||||
const bool print = directive->should_print_memstat();
|
||||
|
||||
if (print) {
|
||||
char buf[1024];
|
||||
fmn.as_C_string(buf, sizeof(buf));
|
||||
tty->print("%s Arena usage %s: ", compilertype2name(ct), buf);
|
||||
arena_stat->print_on(tty);
|
||||
tty->cr();
|
||||
}
|
||||
{
|
||||
MutexLocker ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert(_the_table != nullptr, "not initialized");
|
||||
|
||||
_the_table->add(fmn, ct,
|
||||
arena_stat->peak_since_start(), // total
|
||||
arena_stat->na_at_peak(),
|
||||
arena_stat->ra_at_peak(),
|
||||
arena_stat->live_nodes_at_peak());
|
||||
}
|
||||
}
|
||||
|
||||
void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* arena) {
|
||||
assert(enabled(), "Not enabled?");
|
||||
CompilerThread* const th = Thread::current()->as_Compiler_thread();
|
||||
th->arena_stat()->account(diff, (int)arena->get_tag());
|
||||
}
|
||||
|
||||
static inline ssize_t diff_entries_by_size(const MemStatEntry* e1, const MemStatEntry* e2) {
|
||||
return e1->compare_by_size(e2);
|
||||
}
|
||||
|
||||
void CompilationMemoryStatistic::print_all_by_size(outputStream* st, bool human_readable, size_t min_size) {
|
||||
st->print_cr("Compilation memory statistics");
|
||||
|
||||
if (!enabled()) {
|
||||
st->print_cr("(unavailable)");
|
||||
return;
|
||||
}
|
||||
|
||||
st->cr();
|
||||
|
||||
MemStatEntry::print_legend(st);
|
||||
st->cr();
|
||||
|
||||
if (min_size > 0) {
|
||||
st->print_cr(" (cutoff: %zu bytes)", min_size);
|
||||
}
|
||||
st->cr();
|
||||
|
||||
MemStatEntry::print_header(st);
|
||||
|
||||
MemStatEntry** filtered = nullptr;
|
||||
{
|
||||
MutexLocker ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
if (_the_table != nullptr) {
|
||||
// We sort with quicksort
|
||||
int num = 0;
|
||||
filtered = _the_table->calc_flat_array(num, min_size);
|
||||
if (min_size > 0) {
|
||||
st->print_cr("(%d/%d)", num, _the_table->number_of_entries());
|
||||
}
|
||||
if (num > 0) {
|
||||
QuickSort::sort(filtered, num, diff_entries_by_size, false);
|
||||
// Now print. Has to happen under lock protection too, since entries may be changed.
|
||||
for (int i = 0; i < num; i ++) {
|
||||
filtered[i]->print_on(st, human_readable);
|
||||
}
|
||||
} else {
|
||||
st->print_cr("No entries.");
|
||||
}
|
||||
} else {
|
||||
st->print_cr("Not initialized.");
|
||||
}
|
||||
} // locked
|
||||
|
||||
FREE_C_HEAP_ARRAY(Entry, filtered);
|
||||
}
|
||||
|
||||
CompilationMemoryStatisticMark::CompilationMemoryStatisticMark(const DirectiveSet* directive)
|
||||
: _active(directive->should_collect_memstat()) {
|
||||
if (_active) {
|
||||
CompilationMemoryStatistic::on_start_compilation();
|
||||
}
|
||||
}
|
||||
CompilationMemoryStatisticMark::~CompilationMemoryStatisticMark() {
|
||||
if (_active) {
|
||||
CompilationMemoryStatistic::on_end_compilation();
|
||||
}
|
||||
}
|
103
src/hotspot/share/compiler/compilationMemoryStatistic.hpp
Normal file
103
src/hotspot/share/compiler/compilationMemoryStatistic.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP
|
||||
#define SHARE_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP
|
||||
|
||||
#include "compiler/compilerDefinitions.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
class outputStream;
|
||||
class Symbol;
|
||||
class DirectiveSet;
|
||||
|
||||
// Counters for allocations from one arena
|
||||
class ArenaStatCounter : public CHeapObj<mtCompiler> {
|
||||
// Current bytes, total
|
||||
size_t _current;
|
||||
// bytes when compilation started
|
||||
size_t _start;
|
||||
// bytes at last peak, total
|
||||
size_t _peak;
|
||||
// Current bytes used for node arenas, total
|
||||
size_t _na;
|
||||
// Current bytes used for resource areas
|
||||
size_t _ra;
|
||||
|
||||
// Peak composition:
|
||||
// Size of node arena when total peaked (c2 only)
|
||||
size_t _na_at_peak;
|
||||
// Size of resource area when total peaked
|
||||
size_t _ra_at_peak;
|
||||
// Number of live nodes when total peaked (c2 only)
|
||||
unsigned _live_nodes_at_peak;
|
||||
|
||||
void update_c2_node_count();
|
||||
|
||||
public:
|
||||
ArenaStatCounter();
|
||||
|
||||
// Size of peak since last compilation
|
||||
size_t peak_since_start() const;
|
||||
|
||||
// Peak details
|
||||
size_t na_at_peak() const { return _na_at_peak; }
|
||||
size_t ra_at_peak() const { return _ra_at_peak; }
|
||||
unsigned live_nodes_at_peak() const { return _live_nodes_at_peak; }
|
||||
|
||||
// Mark the start of a compilation.
|
||||
void start();
|
||||
|
||||
// Account an arena allocation or de-allocation.
|
||||
// Returns true if new peak reached
|
||||
bool account(ssize_t delta, int tag);
|
||||
|
||||
void set_live_nodes_at_peak(unsigned i) { _live_nodes_at_peak = i; }
|
||||
void print_on(outputStream* st) const;
|
||||
};
|
||||
|
||||
class CompilationMemoryStatistic : public AllStatic {
|
||||
static bool _enabled;
|
||||
public:
|
||||
static void initialize();
|
||||
// true if CollectMemStat or PrintMemStat has been enabled for any method
|
||||
static bool enabled() { return _enabled; }
|
||||
static void on_start_compilation();
|
||||
static void on_end_compilation();
|
||||
static void on_arena_change(ssize_t diff, const Arena* arena);
|
||||
static void print_all_by_size(outputStream* st, bool human_readable, size_t minsize);
|
||||
};
|
||||
|
||||
// RAII object to wrap one compilation
|
||||
class CompilationMemoryStatisticMark {
|
||||
const bool _active;
|
||||
public:
|
||||
CompilationMemoryStatisticMark(const DirectiveSet* directive);
|
||||
~CompilationMemoryStatisticMark();
|
||||
};
|
||||
|
||||
#endif // SHARE_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP
|
@ -31,6 +31,7 @@
|
||||
#include "code/codeHeapState.hpp"
|
||||
#include "code/dependencyContext.hpp"
|
||||
#include "compiler/compilationLog.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compilationPolicy.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/compileLog.hpp"
|
||||
@ -650,6 +651,10 @@ void CompileBroker::compilation_init(JavaThread* THREAD) {
|
||||
}
|
||||
#endif // INCLUDE_JVMCI
|
||||
|
||||
if (CompilerOracle::should_collect_memstat()) {
|
||||
CompilationMemoryStatistic::initialize();
|
||||
}
|
||||
|
||||
// Start the compiler thread(s)
|
||||
init_compiler_threads();
|
||||
// totalTime performance counter is always created as it is required
|
||||
|
@ -38,7 +38,7 @@ enum CompilerType : u1 {
|
||||
};
|
||||
|
||||
extern const char* compilertype2name_tab[compiler_number_of_types]; // Map CompilerType to its name
|
||||
inline const char* compilertype2name(CompilerType t) { return (uint)t < compiler_number_of_types ? compilertype2name_tab[t] : nullptr; }
|
||||
inline const char* compilertype2name(CompilerType t) { return (uint)t < compiler_number_of_types ? compilertype2name_tab[t] : "invalid"; }
|
||||
|
||||
// Handy constants for deciding which compiler mode to use.
|
||||
enum MethodCompilation {
|
||||
|
@ -202,6 +202,14 @@ bool DirectiveSet::is_c2(CompilerDirectives* directive) const {
|
||||
return this == directive->_c2_store;
|
||||
}
|
||||
|
||||
bool DirectiveSet::should_collect_memstat() const {
|
||||
return MemStatOption > 0;
|
||||
}
|
||||
|
||||
bool DirectiveSet::should_print_memstat() const {
|
||||
return MemStatOption == (uintx)MemStatAction::print;
|
||||
}
|
||||
|
||||
// In the list of Control/disabled intrinsics, the ID of the control intrinsics can separated:
|
||||
// - by ',' (if -XX:Control/DisableIntrinsic is used once when invoking the VM) or
|
||||
// - by '\n' (if -XX:Control/DisableIntrinsic is used multiple times when invoking the VM) or
|
||||
|
@ -41,6 +41,7 @@
|
||||
cflags(BreakAtExecute, bool, false, BreakAtExecute) \
|
||||
cflags(BreakAtCompile, bool, false, BreakAtCompile) \
|
||||
cflags(Log, bool, LogCompilation, Unknown) \
|
||||
cflags(MemStat, uintx, 0, MemStat) \
|
||||
cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \
|
||||
cflags(PrintCompilation, bool, PrintCompilation, PrintCompilation) \
|
||||
cflags(PrintInlining, bool, PrintInlining, PrintInlining) \
|
||||
@ -131,6 +132,8 @@ public:
|
||||
void finalize(outputStream* st);
|
||||
bool is_c1(CompilerDirectives* directive) const;
|
||||
bool is_c2(CompilerDirectives* directive) const;
|
||||
bool should_collect_memstat() const;
|
||||
bool should_print_memstat() const;
|
||||
|
||||
typedef enum {
|
||||
#define enum_of_flags(name, type, dvalue, cc_flag) name##Index,
|
||||
|
@ -101,6 +101,7 @@ class TypedMethodOptionMatcher;
|
||||
|
||||
static TypedMethodOptionMatcher* option_list = nullptr;
|
||||
static bool any_set = false;
|
||||
static bool print_final_memstat_report = false;
|
||||
|
||||
// A filter for quick lookup if an option is set
|
||||
static bool option_filter[static_cast<int>(CompileCommand::Unknown) + 1] = { 0 };
|
||||
@ -325,6 +326,7 @@ static void register_command(TypedMethodOptionMatcher* matcher,
|
||||
tty->print("CompileCommand: %s ", option2name(option));
|
||||
matcher->print();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -455,6 +457,15 @@ bool CompilerOracle::should_print_methods() {
|
||||
return has_command(CompileCommand::Print);
|
||||
}
|
||||
|
||||
// Tells whether there are any methods to collect memory statistics for
|
||||
bool CompilerOracle::should_collect_memstat() {
|
||||
return has_command(CompileCommand::MemStat);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_print_final_memstat_report() {
|
||||
return print_final_memstat_report;
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_log(const methodHandle& method) {
|
||||
if (!LogCompilation) return false;
|
||||
if (!has_command(CompileCommand::Log)) {
|
||||
@ -623,6 +634,22 @@ void skip_comma(char* &line) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool parseEnumValueAsUintx(enum CompileCommand option, const char* line, uintx& value, int& bytes_read, char* errorbuf, const int buf_size) {
|
||||
if (option == CompileCommand::MemStat) {
|
||||
if (strncasecmp(line, "collect", 7) == 0) {
|
||||
value = (uintx)MemStatAction::collect;
|
||||
} else if (strncasecmp(line, "print", 5) == 0) {
|
||||
value = (uintx)MemStatAction::print;
|
||||
print_final_memstat_report = true;
|
||||
} else {
|
||||
jio_snprintf(errorbuf, buf_size, "MemStat: invalid value expected 'collect' or 'print' (omitting value means 'collect')");
|
||||
}
|
||||
return true; // handled
|
||||
}
|
||||
return false;
|
||||
#undef HANDLE_VALUE
|
||||
}
|
||||
|
||||
static void scan_value(enum OptionType type, char* line, int& total_bytes_read,
|
||||
TypedMethodOptionMatcher* matcher, enum CompileCommand option, char* errorbuf, const int buf_size) {
|
||||
int bytes_read = 0;
|
||||
@ -642,7 +669,13 @@ static void scan_value(enum OptionType type, char* line, int& total_bytes_read,
|
||||
}
|
||||
} else if (type == OptionType::Uintx) {
|
||||
uintx value;
|
||||
if (sscanf(line, "" UINTX_FORMAT "%n", &value, &bytes_read) == 1) {
|
||||
// Is it a named enum?
|
||||
bool success = parseEnumValueAsUintx(option, line, value, bytes_read, errorbuf, buf_size);
|
||||
if (!success) {
|
||||
// Is it a raw number?
|
||||
success = (sscanf(line, "" UINTX_FORMAT "%n", &value, &bytes_read) == 1);
|
||||
}
|
||||
if (success) {
|
||||
total_bytes_read += bytes_read;
|
||||
line += bytes_read;
|
||||
register_command(matcher, option, value);
|
||||
@ -914,10 +947,14 @@ bool CompilerOracle::parse_from_line(char* line) {
|
||||
}
|
||||
skip_whitespace(line);
|
||||
if (*line == '\0') {
|
||||
// if this is a bool option this implies true
|
||||
if (option2type(option) == OptionType::Bool) {
|
||||
// if this is a bool option this implies true
|
||||
register_command(matcher, option, true);
|
||||
return true;
|
||||
} else if (option == CompileCommand::MemStat) {
|
||||
// MemStat default action is to collect data but to not print
|
||||
register_command(matcher, option, (uintx)MemStatAction::collect);
|
||||
return true;
|
||||
} else {
|
||||
jio_snprintf(error_buf, sizeof(error_buf), " Option '%s' is not followed by a value", option2name(option));
|
||||
print_parse_error(error_buf, original.get());
|
||||
|
@ -57,6 +57,7 @@ class methodHandle;
|
||||
option(Break, "break", Bool) \
|
||||
option(BreakAtExecute, "BreakAtExecute", Bool) \
|
||||
option(BreakAtCompile, "BreakAtCompile", Bool) \
|
||||
option(MemStat, "MemStat", Uintx) \
|
||||
option(PrintAssembly, "PrintAssembly", Bool) \
|
||||
option(PrintCompilation, "PrintCompilation", Bool) \
|
||||
option(PrintInlining, "PrintInlining", Bool) \
|
||||
@ -113,6 +114,10 @@ enum class OptionType {
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum class MemStatAction {
|
||||
collect = 1, print = 2
|
||||
};
|
||||
|
||||
class CompilerOracle : AllStatic {
|
||||
private:
|
||||
static bool _quiet;
|
||||
@ -151,6 +156,10 @@ class CompilerOracle : AllStatic {
|
||||
// Tells whether there are any methods to print for print_method_statistics()
|
||||
static bool should_print_methods();
|
||||
|
||||
// Tells whether there are any methods to (collect|collect+print) memory statistics for
|
||||
static bool should_collect_memstat();
|
||||
static bool should_print_final_memstat_report();
|
||||
|
||||
// Tags the method as blackhole candidate, if possible.
|
||||
static void tag_blackhole_if_possible(const methodHandle& method);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/compileTask.hpp"
|
||||
#include "compiler/compilerThread.hpp"
|
||||
@ -39,6 +40,7 @@ CompilerThread::CompilerThread(CompileQueue* queue,
|
||||
_counters = counters;
|
||||
_buffer_blob = nullptr;
|
||||
_compiler = nullptr;
|
||||
_arena_stat = CompilationMemoryStatistic::enabled() ? new ArenaStatCounter : nullptr;
|
||||
|
||||
// Compiler uses resource area for compilation, let's bias it to mtCompiler
|
||||
resource_area()->bias_to(mtCompiler);
|
||||
@ -51,6 +53,7 @@ CompilerThread::CompilerThread(CompileQueue* queue,
|
||||
CompilerThread::~CompilerThread() {
|
||||
// Delete objects which were allocated on heap.
|
||||
delete _counters;
|
||||
delete _arena_stat;
|
||||
}
|
||||
|
||||
void CompilerThread::thread_entry(JavaThread* thread, TRAPS) {
|
||||
|
@ -27,8 +27,9 @@
|
||||
|
||||
#include "runtime/javaThread.hpp"
|
||||
|
||||
class BufferBlob;
|
||||
class AbstractCompiler;
|
||||
class ArenaStatCounter;
|
||||
class BufferBlob;
|
||||
class ciEnv;
|
||||
class CompileThread;
|
||||
class CompileLog;
|
||||
@ -54,6 +55,8 @@ class CompilerThread : public JavaThread {
|
||||
AbstractCompiler* _compiler;
|
||||
TimeStamp _idle_time;
|
||||
|
||||
ArenaStatCounter* _arena_stat;
|
||||
|
||||
public:
|
||||
|
||||
static CompilerThread* current() {
|
||||
@ -81,6 +84,7 @@ class CompilerThread : public JavaThread {
|
||||
|
||||
CompileQueue* queue() const { return _queue; }
|
||||
CompilerCounters* counters() const { return _counters; }
|
||||
ArenaStatCounter* arena_stat() const { return _arena_stat; }
|
||||
|
||||
// Get/set the thread's compilation environment.
|
||||
ciEnv* env() { return _env; }
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/arena.hpp"
|
||||
@ -209,7 +210,7 @@ void Chunk::next_chop(Chunk* k) {
|
||||
k->_next = nullptr;
|
||||
}
|
||||
|
||||
Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) {
|
||||
Arena::Arena(MEMFLAGS flag, Tag tag, size_t init_size) : _flags(flag), _tag(tag), _size_in_bytes(0) {
|
||||
init_size = ARENA_ALIGN(init_size);
|
||||
_chunk = ChunkPool::allocate_chunk(init_size, AllocFailStrategy::EXIT_OOM);
|
||||
_first = _chunk;
|
||||
@ -219,7 +220,7 @@ Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0)
|
||||
set_size_in_bytes(init_size);
|
||||
}
|
||||
|
||||
Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) {
|
||||
Arena::Arena(MEMFLAGS flag, Tag tag) : _flags(flag), _tag(tag), _size_in_bytes(0) {
|
||||
_chunk = ChunkPool::allocate_chunk(Chunk::init_size, AllocFailStrategy::EXIT_OOM);
|
||||
_first = _chunk;
|
||||
_hwm = _chunk->bottom(); // Save the cached hwm, max
|
||||
@ -251,6 +252,12 @@ void Arena::set_size_in_bytes(size_t size) {
|
||||
ssize_t delta = size - size_in_bytes();
|
||||
_size_in_bytes = size;
|
||||
MemTracker::record_arena_size_change(delta, _flags);
|
||||
if (CompilationMemoryStatistic::enabled() && _flags == mtCompiler) {
|
||||
Thread* const t = Thread::current();
|
||||
if (t != nullptr && t->is_Compiler_thread()) {
|
||||
CompilationMemoryStatistic::on_arena_change(delta, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,13 +86,22 @@ public:
|
||||
|
||||
// Fast allocation of memory
|
||||
class Arena : public CHeapObjBase {
|
||||
public:
|
||||
|
||||
enum class Tag {
|
||||
tag_other = 0,
|
||||
tag_ra, // resource area
|
||||
tag_ha, // handle area
|
||||
tag_node // C2 Node arena
|
||||
};
|
||||
|
||||
protected:
|
||||
friend class HandleMark;
|
||||
friend class NoHandleMark;
|
||||
friend class VMStructs;
|
||||
|
||||
MEMFLAGS _flags; // Memory tracking flags
|
||||
|
||||
const Tag _tag;
|
||||
Chunk* _first; // First chunk
|
||||
Chunk* _chunk; // current chunk
|
||||
char* _hwm; // High water mark
|
||||
@ -115,9 +124,8 @@ protected:
|
||||
public:
|
||||
// Start the chunk_pool cleaner task
|
||||
static void start_chunk_pool_cleaner_task();
|
||||
|
||||
Arena(MEMFLAGS memflag);
|
||||
Arena(MEMFLAGS memflag, size_t init_size);
|
||||
Arena(MEMFLAGS memflag, Tag tag = Tag::tag_other);
|
||||
Arena(MEMFLAGS memflag, Tag tag, size_t init_size);
|
||||
~Arena();
|
||||
void destruct_contents();
|
||||
char* hwm() const { return _hwm; }
|
||||
@ -171,6 +179,8 @@ protected:
|
||||
size_t size_in_bytes() const { return _size_in_bytes; };
|
||||
void set_size_in_bytes(size_t size);
|
||||
|
||||
Tag get_tag() const { return _tag; }
|
||||
|
||||
private:
|
||||
// Reset this Arena to empty, access will trigger grow if necessary
|
||||
void reset(void) {
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define SHARE_MEMORY_RESOURCEAREA_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/arena.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
|
||||
// The resource area holds temporary data structures in the VM.
|
||||
@ -51,10 +52,11 @@ class ResourceArea: public Arena {
|
||||
|
||||
public:
|
||||
ResourceArea(MEMFLAGS flags = mtThread) :
|
||||
Arena(flags) DEBUG_ONLY(COMMA _nesting(0)) {}
|
||||
Arena(flags, Arena::Tag::tag_ra) DEBUG_ONLY(COMMA _nesting(0)) {}
|
||||
|
||||
ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) :
|
||||
Arena(flags, init_size) DEBUG_ONLY(COMMA _nesting(0)) {}
|
||||
Arena(flags, Arena::Tag::tag_ra, init_size) DEBUG_ONLY(COMMA _nesting(0)) {
|
||||
}
|
||||
|
||||
char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compilerDefinitions.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "jfr/support/jfrIntrinsics.hpp"
|
||||
@ -109,6 +110,8 @@ void C2Compiler::initialize() {
|
||||
void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) {
|
||||
assert(is_initialized(), "Compiler thread must be initialized");
|
||||
|
||||
CompilationMemoryStatisticMark cmsm(directive);
|
||||
|
||||
bool subsume_loads = SubsumeLoads;
|
||||
bool do_escape_analysis = DoEscapeAnalysis;
|
||||
bool do_iterative_escape_analysis = DoEscapeAnalysis;
|
||||
|
@ -646,8 +646,8 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
_unique(0),
|
||||
_dead_node_count(0),
|
||||
_dead_node_list(comp_arena()),
|
||||
_node_arena_one(mtCompiler),
|
||||
_node_arena_two(mtCompiler),
|
||||
_node_arena_one(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena_two(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena(&_node_arena_one),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler),
|
||||
|
@ -728,7 +728,7 @@ WB_ENTRY(jint, WB_NMTGetHashSize(JNIEnv* env, jobject o))
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(jlong, WB_NMTNewArena(JNIEnv* env, jobject o, jlong init_size))
|
||||
Arena* arena = new (mtTest) Arena(mtTest, size_t(init_size));
|
||||
Arena* arena = new (mtTest) Arena(mtTest, Arena::Tag::tag_other, size_t(init_size));
|
||||
return (jlong)arena;
|
||||
WB_END
|
||||
|
||||
|
@ -187,7 +187,7 @@ class HandleArea: public Arena {
|
||||
HandleArea* _prev; // link to outer (older) area
|
||||
public:
|
||||
// Constructor
|
||||
HandleArea(HandleArea* prev) : Arena(mtThread, Chunk::tiny_size) {
|
||||
HandleArea(HandleArea* prev) : Arena(mtThread, Tag::tag_ha, Chunk::tiny_size) {
|
||||
debug_only(_handle_mark_nesting = 0);
|
||||
debug_only(_no_handle_mark_nesting = 0);
|
||||
_prev = prev;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/compilerOracle.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
@ -341,6 +342,10 @@ void print_statistics() {
|
||||
MetaspaceUtils::print_basic_report(tty, 0);
|
||||
}
|
||||
|
||||
if (CompilerOracle::should_print_final_memstat_report()) {
|
||||
CompilationMemoryStatistic::print_all_by_size(tty, false, 0);
|
||||
}
|
||||
|
||||
ThreadsSMRSupport::log_statistics();
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,7 @@ Mutex* ThreadIdTableCreate_lock = nullptr;
|
||||
Mutex* SharedDecoder_lock = nullptr;
|
||||
Mutex* DCmdFactory_lock = nullptr;
|
||||
Mutex* NMTQuery_lock = nullptr;
|
||||
Mutex* NMTCompilationCostHistory_lock = nullptr;
|
||||
|
||||
#if INCLUDE_CDS
|
||||
#if INCLUDE_JVMTI
|
||||
@ -309,6 +310,7 @@ void mutex_init() {
|
||||
MUTEX_DEFN(SharedDecoder_lock , PaddedMutex , tty-1);
|
||||
MUTEX_DEFN(DCmdFactory_lock , PaddedMutex , nosafepoint);
|
||||
MUTEX_DEFN(NMTQuery_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(NMTCompilationCostHistory_lock , PaddedMutex , nosafepoint);
|
||||
#if INCLUDE_CDS
|
||||
#if INCLUDE_JVMTI
|
||||
MUTEX_DEFN(CDSClassFileStream_lock , PaddedMutex , safepoint);
|
||||
|
@ -117,6 +117,7 @@ extern Mutex* ThreadIdTableCreate_lock; // Used by ThreadIdTable to laz
|
||||
extern Mutex* SharedDecoder_lock; // serializes access to the decoder during normal (not error reporting) use
|
||||
extern Mutex* DCmdFactory_lock; // serialize access to DCmdFactory information
|
||||
extern Mutex* NMTQuery_lock; // serialize NMT Dcmd queries
|
||||
extern Mutex* NMTCompilationCostHistory_lock; // guards NMT compilation cost history
|
||||
#if INCLUDE_CDS
|
||||
#if INCLUDE_JVMTI
|
||||
extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "jfr/support/jfrThreadExtension.hpp"
|
||||
#endif
|
||||
|
||||
class CompilerThread;
|
||||
class HandleArea;
|
||||
class HandleMark;
|
||||
class ICRefillVerifier;
|
||||
@ -324,6 +325,12 @@ class Thread: public ThreadShadow {
|
||||
virtual bool is_AttachListener_thread() const { return false; }
|
||||
virtual bool is_monitor_deflation_thread() const { return false; }
|
||||
|
||||
// Convenience cast functions
|
||||
CompilerThread* as_Compiler_thread() const {
|
||||
assert(is_Compiler_thread(), "Must be compiler thread");
|
||||
return (CompilerThread*)this;
|
||||
}
|
||||
|
||||
// Can this thread make Java upcalls
|
||||
virtual bool can_call_java() const { return false; }
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "compiler/compilationMemoryStatistic.hpp"
|
||||
#include "compiler/compiler_globals.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/directivesParser.hpp"
|
||||
#include "gc/shared/gcVMOperations.hpp"
|
||||
@ -138,6 +140,7 @@ void DCmd::register_dcmds(){
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesAddDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesRemoveDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesClearDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilationMemoryStatisticDCmd>(full_export, true, false));
|
||||
|
||||
// Enhanced JMX Agent Support
|
||||
// These commands won't be exported via the DiagnosticCommandMBean until an
|
||||
@ -1134,3 +1137,17 @@ void ThreadDumpToFileDCmd::dumpToFile(Symbol* name, Symbol* signature, const cha
|
||||
jbyte* addr = typeArrayOop(res)->byte_at_addr(0);
|
||||
output()->print_raw((const char*)addr, ba->length());
|
||||
}
|
||||
|
||||
CompilationMemoryStatisticDCmd::CompilationMemoryStatisticDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_human_readable("-H", "Human readable format", "BOOLEAN", false, "false"),
|
||||
_minsize("-s", "Minimum memory size", "MEMORY SIZE", false, "0") {
|
||||
_dcmdparser.add_dcmd_option(&_human_readable);
|
||||
_dcmdparser.add_dcmd_option(&_minsize);
|
||||
}
|
||||
|
||||
void CompilationMemoryStatisticDCmd::execute(DCmdSource source, TRAPS) {
|
||||
const bool human_readable = _human_readable.value();
|
||||
const size_t minsize = _minsize.has_value() ? _minsize.value()._size : 0;
|
||||
CompilationMemoryStatistic::print_all_by_size(output(), human_readable, minsize);
|
||||
}
|
||||
|
@ -954,4 +954,28 @@ public:
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
class CompilationMemoryStatisticDCmd: public DCmdWithParser {
|
||||
protected:
|
||||
DCmdArgument<bool> _human_readable;
|
||||
DCmdArgument<MemorySizeArgument> _minsize;
|
||||
public:
|
||||
static int num_arguments() { return 2; }
|
||||
CompilationMemoryStatisticDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "Compiler.memory";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Print compilation footprint";
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Pause time depends on number of compiled methods";
|
||||
}
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"monitor", nullptr};
|
||||
return p;
|
||||
}
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_SERVICES_DIAGNOSTICCOMMAND_HPP
|
||||
|
@ -329,7 +329,7 @@ TEST_VM(Arena, mixed_alignment_allocation) {
|
||||
TEST_VM(Arena, Arena_with_crooked_initial_size) {
|
||||
// Test that an arena with a crooked, not 64-bit aligned initial size
|
||||
// works
|
||||
Arena ar(mtTest, 4097);
|
||||
Arena ar(mtTest, Arena::Tag::tag_other, 4097);
|
||||
void* p1 = ar.AmallocWords(BytesPerWord);
|
||||
void* p2 = ar.Amalloc(BytesPerLong);
|
||||
ASSERT_TRUE(is_aligned(p1, BytesPerWord));
|
||||
@ -342,7 +342,7 @@ TEST_VM(Arena, Arena_grows_large_unaligned) {
|
||||
// (only possible on 32-bit when allocating with word alignment).
|
||||
// Then we alloc some more. If Arena::grow() does not correctly align, on 32-bit
|
||||
// something should assert at some point.
|
||||
Arena ar(mtTest, 100); // first chunk is small
|
||||
Arena ar(mtTest, Arena::Tag::tag_other, 100); // first chunk is small
|
||||
void* p = ar.AmallocWords(Chunk::size + BytesPerWord); // if Arena::grow() misaligns, this asserts
|
||||
// some more allocations for good measure
|
||||
for (int i = 0; i < 100; i ++) {
|
||||
@ -372,13 +372,13 @@ TEST_VM(Arena, different_chunk_sizes) {
|
||||
for (int i = 0; i < 1000; i ++) {
|
||||
// Unfortunately, Arenas cannot be newed,
|
||||
// so we are left with awkwardly placing a few on the stack.
|
||||
Arena ar0(mtTest, random_arena_chunk_size());
|
||||
Arena ar1(mtTest, random_arena_chunk_size());
|
||||
Arena ar2(mtTest, random_arena_chunk_size());
|
||||
Arena ar3(mtTest, random_arena_chunk_size());
|
||||
Arena ar4(mtTest, random_arena_chunk_size());
|
||||
Arena ar5(mtTest, random_arena_chunk_size());
|
||||
Arena ar6(mtTest, random_arena_chunk_size());
|
||||
Arena ar7(mtTest, random_arena_chunk_size());
|
||||
Arena ar0(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar1(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar2(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar3(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar4(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar5(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar6(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
Arena ar7(mtTest, Arena::Tag::tag_other, random_arena_chunk_size());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Checks that -XX:CompileCommand=PrintMemStat,... works
|
||||
* @library /test/lib
|
||||
* @run driver compiler.print.CompileCommandPrintMemStat
|
||||
*/
|
||||
|
||||
package compiler.print;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CompileCommandPrintMemStat {
|
||||
|
||||
final static String METHOD1 = "method1";
|
||||
final static String METHOD2 = "method2";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
test(METHOD1, METHOD2);
|
||||
test(METHOD2, METHOD1);
|
||||
}
|
||||
|
||||
private static void test(String include, String exclude) throws Exception {
|
||||
List<String> options = new ArrayList<String>();
|
||||
options.add("-Xcomp");
|
||||
options.add("-XX:-Inline");
|
||||
options.add("-XX:CompileCommand=compileonly," + getTestClass() + "::*");
|
||||
options.add("-XX:CompileCommand=MemStat," + getTestMethod(include) + ",print");
|
||||
options.add(getTestClass());
|
||||
|
||||
OutputAnalyzer oa = ProcessTools.executeTestJvm(options);
|
||||
|
||||
// We expect two printouts for "PrintMemStat". A line at compilation time, and a line in a summary report
|
||||
// that is printed when we exit. Both use the typical <class>::name format but use / as separator and also
|
||||
// print the signature.
|
||||
String expectedNameIncl = getTestMethod(include)
|
||||
.replace('.', '/')
|
||||
.replace("$", "\\$");
|
||||
String expectedNameExcl = getTestMethod(exclude)
|
||||
.replace('.', '/')
|
||||
.replace("$", "\\$");
|
||||
|
||||
// Should see trace output when methods are compiled
|
||||
oa.shouldHaveExitValue(0)
|
||||
.shouldMatch(".*" + expectedNameIncl + ".*")
|
||||
.shouldNotMatch(".*" + expectedNameExcl + ".*");
|
||||
|
||||
// Should see final report
|
||||
// Looks like this:
|
||||
// total NA RA #nodes time type #rc thread method
|
||||
// 621832 0 589104 0 0,025 c1 1 0x00007f5ccc1951a0 java/util/zip/ZipFile$Source::checkAndAddEntry((II)I)
|
||||
oa.shouldMatch("total.*method");
|
||||
oa.shouldMatch("\\d+ +\\d+ +\\d+ +\\d+.*" + expectedNameIncl + ".*");
|
||||
oa.shouldNotMatch("\\d+ +\\d+ +\\d+ +\\d+.*" + expectedNameExcl + ".*");
|
||||
}
|
||||
|
||||
// Test class that is invoked by the sub process
|
||||
public static String getTestClass() {
|
||||
return TestMain.class.getName();
|
||||
}
|
||||
|
||||
public static String getTestMethod(String method) {
|
||||
return getTestClass() + "::" + method;
|
||||
}
|
||||
|
||||
public static class TestMain {
|
||||
public static void main(String[] args) {
|
||||
method1();
|
||||
method2();
|
||||
}
|
||||
|
||||
static void method1() {}
|
||||
static void method2() {}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.dcmd.PidJcmdExecutor;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/*
|
||||
* @test CompilerMemoryStatisticTest
|
||||
* @summary Test Compiler.memory
|
||||
* @requires vm.compiler1.enabled
|
||||
* @requires vm.compiler2.enabled
|
||||
*
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run main/othervm -XX:CompileCommand=memstat,*.* CompilerMemoryStatisticTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test CompilerMemoryStatisticTest
|
||||
* @summary Test Compiler.memory
|
||||
* @requires vm.compiler1.enabled
|
||||
* @requires vm.compiler2.enabled
|
||||
*
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run main/othervm -XX:CompileCommand=memstat,*.*,collect CompilerMemoryStatisticTest
|
||||
*/
|
||||
|
||||
public class CompilerMemoryStatisticTest {
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
PidJcmdExecutor executor = new PidJcmdExecutor();
|
||||
OutputAnalyzer out = executor.execute("Compiler.memory");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// Looks like this:
|
||||
// total NA RA #nodes time type #rc thread method
|
||||
// 621832 0 589104 0 0,025 c1 1 0x00007f5ccc1951a0 java/util/zip/ZipFile$Source.checkAndAddEntry((II)I)
|
||||
out.shouldMatch("total.*method");
|
||||
out.shouldMatch("\\d+ +\\d+ +\\d+ +\\d+.*java.*\\(.*\\)");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user