diff --git a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp index fd1902b13d8..fc54e64e12c 100644 --- a/src/hotspot/share/c1/c1_Compilation.cpp +++ b/src/hotspot/share/c1/c1_Compilation.cpp @@ -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" @@ -427,6 +430,9 @@ void Compilation::install_code(int frame_size) { void Compilation::compile_method() { + + CompilationMemoryStatisticMark cmsm(directive()); + { PhaseTraceTime timeit(_t_setup); diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index edc7ed8601d..4b04197e00a 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -76,7 +76,7 @@ void SymbolTable::initialize_symbols(int arena_alloc_size) { if (arena_alloc_size == 0) { _arena = new (mtSymbol) Arena(mtSymbol); } else { - _arena = new (mtSymbol) Arena(mtSymbol, arena_alloc_size); + _arena = new (mtSymbol) Arena(mtSymbol, tag_other, symbol_alloc_arena_size); } } diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp new file mode 100644 index 00000000000..d35c396df2b --- /dev/null +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -0,0 +1,489 @@ +/* + * 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 "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 != NULL && + th->task()->compiler() != NULL && + th->task()->compiler()->type() == compiler_c2) { + const Compile* const comp = Compile::current(); + if (comp != NULL) { + _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 ((MEMTAG)tag) { + case tag_ra: _ra += delta; break; + case 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() { + /* FIXME + _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); + */ + return n._k->identity_hash() ^ + n._m->identity_hash() ^ + n._s->identity_hash(); + } + + static bool fmn_equals(const FullMethodName& x1, const FullMethodName& x2) { + return x1.equals(x2); + } + + 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 { + 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(NULL), + _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; + +class MemStatTableIterator { + size_t _min_size; + MemStatEntry** _flat; + int& _index; + int _num_all; + +public: + MemStatTableIterator(size_t min_size, MemStatEntry** flat, int& index, int num_all): _min_size(min_size), _flat(flat), _index(index), _num_all(num_all) {} + + bool do_entry(const FullMethodName& ignored, MemStatEntry* e) { + if (e->total() >= _min_size) { + _flat[_index] = e; + assert(_index < _num_all, "Sanity"); + _index ++; + } + return true; + } +}; + +class MemStatTable : + public ResourceHashtable +{ +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 = NULL; + if (pe == NULL) { + e = new MemStatEntry(fmn); + put(fmn, e); + } else { + // Update existing entry + e = *pe; + assert(e != NULL, "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); + */ + MemStatTableIterator closure(min_size, flat, i, num_all); + iterate(&closure); + 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 = NULL; + +void CompilationMemoryStatistic::initialize() { + assert(_enabled == false && _the_table == NULL, "Only once"); + // _the_table = new (mtCompiler) MemStatTable; + _the_table = new (ResourceObj::C_HEAP, 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(); + } + { + MutexLockerEx ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag); + assert(_the_table != NULL, "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 = NULL; + { + MutexLockerEx ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag); + + if (_the_table != NULL) { + // 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(); + } +} diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp new file mode 100644 index 00000000000..129159c338d --- /dev/null +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp @@ -0,0 +1,102 @@ +/* + * 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 "utilities/globalDefinitions.hpp" + +class outputStream; +class Symbol; +class DirectiveSet; + +// Counters for allocations from one arena +class ArenaStatCounter : public CHeapObj { + // 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 diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index c3fae3df2c2..a756d203b34 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -31,6 +31,7 @@ #include "code/codeHeapState.hpp" #include "code/dependencyContext.hpp" #include "compiler/compileBroker.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compileLog.hpp" #include "compiler/compilerOracle.hpp" #include "compiler/directivesParser.hpp" @@ -650,6 +651,10 @@ void CompileBroker::compilation_init_phase1(TRAPS) { } } #endif // COMPILER2 + // + if (CompilerOracle::should_collect_memstat()) { + CompilationMemoryStatistic::initialize(); + } // Start the compiler thread(s) and the sweeper thread init_compiler_sweeper_threads(); @@ -2180,6 +2185,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { locker.wait(Mutex::_no_safepoint_check_flag); } } + task->set_directive(directive); // for memory statistic comp->compile_method(&ci_env, target, osr_bci, directive); } diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp index 36760375641..bdc052e039b 100644 --- a/src/hotspot/share/compiler/compileTask.cpp +++ b/src/hotspot/share/compiler/compileTask.cpp @@ -110,6 +110,7 @@ void CompileTask::initialize(int compile_id, _time_started = 0; _compile_reason = compile_reason; _failure_reason = NULL; + _directive = NULL; if (LogCompilation) { if (hot_method.not_null()) { diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index 2029defdc7d..d99b4b6c658 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -101,6 +101,7 @@ class CompileTask : public CHeapObj { int _hot_count; // information about its invocation counter CompileReason _compile_reason; // more info about the task const char* _failure_reason; + DirectiveSet* _directive; public: CompileTask() { @@ -131,6 +132,8 @@ class CompileTask : public CHeapObj { return false; } } + DirectiveSet* directive() const { return _directive; } + void set_directive(DirectiveSet* v) { _directive = v; } #if INCLUDE_JVMCI bool should_wait_for_compilation() const { // Wait for blocking compilation to finish. diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index 12589e11c2e..25aed037696 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -37,7 +37,7 @@ enum CompilerType { }; 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] : NULL; } +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 { diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index 8b91cafe1c0..17e896c0352 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -179,6 +179,14 @@ DirectiveSet* CompilerDirectives::get_for(AbstractCompiler *comp) { } } +bool DirectiveSet::should_collect_memstat() const { + return MemStatOption; +} + +bool DirectiveSet::should_print_memstat() const { + return MemStatOption; +} + // In the list of disabled intrinsics, the ID of the disabled intrinsics can separated: // - by ',' (if -XX:DisableIntrinsic is used once when invoking the VM) or // - by '\n' (if -XX:DisableIntrinsic is used multiple times when invoking the VM) or @@ -280,6 +288,14 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle } } + // MemStat + if (CompilerOracle::should_collect_memstat(method)) { + if (!_modified[MemStatIndex]) { + set->MemStatOption = true; + changed = true; + } + } + // Read old value of DisableIntrinsicOption, in case we need to free it // and overwrite it with a new value. ccstrlist old_disable_intrinsic_value = set->DisableIntrinsicOption; diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp index e2861bb6044..4286d4dd548 100644 --- a/src/hotspot/share/compiler/compilerDirectives.hpp +++ b/src/hotspot/share/compiler/compilerDirectives.hpp @@ -38,6 +38,7 @@ cflags(BreakAtExecute, bool, false, X) \ cflags(BreakAtCompile, bool, false, X) \ cflags(Log, bool, LogCompilation, X) \ + cflags(MemStat, bool, false, X) \ cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \ cflags(PrintInlining, bool, PrintInlining, PrintInlining) \ cflags(PrintNMethods, bool, PrintNMethods, PrintNMethods) \ @@ -116,6 +117,8 @@ class DirectiveSet : public CHeapObj { bool is_intrinsic_disabled(const methodHandle& method); static ccstrlist canonicalize_disableintrinsic(ccstrlist option_value); void finalize(outputStream* st); + bool should_collect_memstat() const; + bool should_print_memstat() const; typedef enum { #define enum_of_flags(name, type, dvalue, cc_flag) name##Index, diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index befe4427e85..f7e67005480 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -83,6 +83,7 @@ enum OracleCommand { CompileOnlyCommand, LogCommand, OptionCommand, + MemStatCommand, QuietCommand, HelpCommand, OracleCommandCount @@ -98,6 +99,7 @@ static const char * command_names[] = { "compileonly", "log", "option", + "MemStat", "quiet", "help" }; @@ -108,6 +110,7 @@ class TypedMethodOptionMatcher; static BasicMatcher* lists[OracleCommandCount] = { 0, }; static TypedMethodOptionMatcher* option_list = NULL; static bool any_set = false; +static bool print_final_memstat_report = false; class TypedMethodOptionMatcher : public MethodMatcher { private: @@ -363,12 +366,39 @@ bool CompilerOracle::should_print_methods() { return lists[PrintCommand] != NULL; } +// Tells whether there are any methods to collect memory statistics for +bool CompilerOracle::should_collect_memstat(const methodHandle& method) { + return check_predicate(MemStatCommand, method); +} + +bool CompilerOracle::should_print_final_memstat_report(const methodHandle& method) { + // TODO: + return check_predicate(MemStatCommand, method); +} + bool CompilerOracle::should_log(const methodHandle& method) { if (!LogCompilation) return false; if (lists[LogCommand] == NULL) return true; // by default, log all return (check_predicate(LogCommand, method)); } +/* +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; +} +*/ + bool CompilerOracle::should_break_at(const methodHandle& method) { return check_predicate(BreakCommand, method); } @@ -382,6 +412,9 @@ static OracleCommand parse_command_name(const char * line, int* bytes_read) { int matches = sscanf(line, "%32[a-z]%n", command, bytes_read); if (matches > 0) { for (uint i = 0; i < ARRAY_SIZE(command_names); i++) { + if (strcmp(command, command_names[MemStatCommand]) == 0) { + CompilerOracle::_collect_mem_stat = true; + } if (strcmp(command, command_names[i]) == 0) { return (OracleCommand)i; } @@ -552,6 +585,7 @@ int skip_whitespace(char* line) { return whitespace_read; } + void CompilerOracle::print_parse_error(const char*& error_msg, char* original_line) { assert(error_msg != NULL, "Must have error_message"); @@ -696,6 +730,7 @@ bool CompilerOracle::has_command_file() { } bool CompilerOracle::_quiet = false; +bool CompilerOracle::_collect_mem_stat = false; void CompilerOracle::parse_from_file() { assert(has_command_file(), "command file must be specified"); diff --git a/src/hotspot/share/compiler/compilerOracle.hpp b/src/hotspot/share/compiler/compilerOracle.hpp index 9b731ebb2e0..5e774729d3f 100644 --- a/src/hotspot/share/compiler/compilerOracle.hpp +++ b/src/hotspot/share/compiler/compilerOracle.hpp @@ -28,6 +28,10 @@ #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" +enum MemStatAction { + collect = 1, print = 2 +}; + // CompilerOracle is an interface for turning on and off compilation // for some methods @@ -38,6 +42,7 @@ class CompilerOracle : AllStatic { static void print_parse_error(const char*& error_msg, char* original_line); public: + static bool _collect_mem_stat; // True if the command file has been specified or is implicit static bool has_command_file(); @@ -58,6 +63,11 @@ class CompilerOracle : AllStatic { // Tells whether we should print the assembly for this method static bool should_print(const methodHandle& method); + // Tells whether there are any methods to (collect|collect+print) memory statistics for + static bool should_collect_memstat() { return _collect_mem_stat; } + static bool should_collect_memstat(const methodHandle& method); + static bool should_print_final_memstat_report(const methodHandle& method); + // Tells whether we should log the compilation data for this method static bool should_log(const methodHandle& method); diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 34457371df1..fb55a37b045 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -143,6 +143,14 @@ enum MemoryType { typedef MemoryType MEMFLAGS; +enum ArenaTag { + tag_other = 0, + tag_ra, // resource area + tag_ha, // handle area + tag_node // C2 Node arena +}; + +typedef ArenaTag MEMTAG; #if INCLUDE_NMT diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index c478d0ba906..b332a6be2d5 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" #include "memory/metaspaceShared.hpp" @@ -246,7 +247,7 @@ void Chunk::start_chunk_pool_cleaner_task() { //------------------------------Arena------------------------------------------ -Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) { +Arena::Arena(MEMFLAGS flag, MEMTAG tag, size_t init_size) : _flags(flag), _tag(tag), _size_in_bytes(0) { size_t round_size = (sizeof (char *)) - 1; init_size = (init_size+round_size) & ~round_size; _first = _chunk = new (AllocFailStrategy::EXIT_OOM, init_size) Chunk(init_size); @@ -256,7 +257,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, MEMTAG tag) : _flags(flag), _tag(tag), _size_in_bytes(0) { _first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size); _hwm = _chunk->bottom(); // Save the cached hwm, max _max = _chunk->top(); @@ -331,6 +332,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 != NULL && t->is_Compiler_thread()) { + CompilationMemoryStatistic::on_arena_change(delta, this); + } + } } } diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 7b84fb5cf26..49f70bbe9e5 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -93,6 +93,8 @@ class WispPostStealHandleUpdateMark; //------------------------------Arena------------------------------------------ // Fast allocation of memory class Arena : public CHeapObj { +public: + protected: friend class ResourceMark; friend class HandleMark; @@ -102,6 +104,7 @@ class Arena : public CHeapObj { MEMFLAGS _flags; // Memory tracking flags + const MEMTAG _tag; Chunk *_first; // First chunk Chunk *_chunk; // current chunk char *_hwm, *_max; // High water mark and max in current chunk @@ -129,8 +132,8 @@ class Arena : public CHeapObj { } public: - Arena(MEMFLAGS memflag); - Arena(MEMFLAGS memflag, size_t init_size); + Arena(MEMFLAGS memflag, MEMTAG tag = tag_other); + Arena(MEMFLAGS memflag, MEMTAG tag, size_t init_size); ~Arena(); void destruct_contents(); char* hwm() const { return _hwm; } @@ -229,6 +232,8 @@ class Arena : public CHeapObj { size_t size_in_bytes() const { return _size_in_bytes; }; void set_size_in_bytes(size_t size); + MEMTAG get_tag() const { return _tag; } + static void free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) PRODUCT_RETURN; static void free_all(char** start, char** end) PRODUCT_RETURN; diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp index 3a5f7e04a76..7c1a2806aa7 100644 --- a/src/hotspot/share/memory/resourceArea.hpp +++ b/src/hotspot/share/memory/resourceArea.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_MEMORY_RESOURCEAREA_HPP #include "memory/allocation.hpp" +#include "memory/arena.hpp" #include "runtime/thread.hpp" // The resource area holds temporary data structures in the VM. @@ -52,11 +53,11 @@ class ResourceArea: public Arena { debug_only(static int _warned;) // to suppress multiple warnings public: - ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) { + ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags, tag_ra) { debug_only(_nesting = 0;) } - ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, init_size) { + ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, tag_ra, init_size) { debug_only(_nesting = 0;); } diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 67cbc582919..2d789f4afc7 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "jfr/support/jfrIntrinsics.hpp" #include "opto/c2compiler.hpp" #include "opto/compile.hpp" @@ -103,6 +104,8 @@ void C2Compiler::initialize() { void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) { assert(is_initialized(), "Compiler thread must be initialized"); + CompilationMemoryStatisticMark cmsm(directive); + bool subsume_loads = SubsumeLoads; bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables() && !env->jvmti_can_get_owned_monitor_info(); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index b7afde9a319..0126667b927 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -687,8 +687,8 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr NOT_PRODUCT(_printer(NULL) COMMA) _congraph(NULL), _comp_arena(mtCompiler), - _node_arena(mtCompiler), - _old_arena(mtCompiler), + _node_arena(mtCompiler, tag_node), + _old_arena(mtCompiler, tag_node), _Compile_types(mtCompiler), _replay_inline_data(NULL), _late_inlines(comp_arena(), 2, 0, NULL), diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 509e7268368..fe51b7bddeb 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -687,7 +687,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, tag_other, size_t(init_size)); return (jlong)arena; WB_END diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index d312069d181..fd5f0d749f9 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -183,13 +183,13 @@ 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_ha, Chunk::tiny_size) { debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); _prev = prev; } // Only coroutine uses this constructor - HandleArea(HandleArea* prev, size_t init_size) : Arena(mtThread, init_size) { + HandleArea(HandleArea* prev, size_t init_size) : Arena(mtThread, tag_ha, init_size) { assert(EnableCoroutine, "EnableCoroutine is off"); debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 1100512c991..7e9bc4f9851 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -29,6 +29,7 @@ #include "classfile/stringTable.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerOracle.hpp" #include "interpreter/bytecodeHistogram.hpp" @@ -384,6 +385,10 @@ void print_statistics() { MetaspaceUtils::print_basic_report(tty, 0); } + if (CompilerOracle::should_collect_memstat()) { + CompilationMemoryStatistic::print_all_by_size(tty, false, 0); + } + ThreadsSMRSupport::log_statistics(); } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 2d99b22bb39..cc0cf02eeb7 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -155,6 +155,7 @@ Mutex* ThreadIdTableCreate_lock = NULL; Monitor* ThreadsSMRDelete_lock = NULL; Mutex* SharedDecoder_lock = NULL; Mutex* DCmdFactory_lock = NULL; +Mutex* NMTCompilationCostHistory_lock = NULL; SystemDictMonitor* SystemDictionary_lock = NULL; @@ -394,6 +395,7 @@ void mutex_init() { def(ThreadsSMRDelete_lock , PaddedMonitor, special, false, Monitor::_safepoint_check_never); def(SharedDecoder_lock , PaddedMutex , native, false, Monitor::_safepoint_check_never); def(DCmdFactory_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_never); + def(NMTCompilationCostHistory_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_never); #if INCLUDE_CDS && INCLUDE_JVMTI def(CDSClassFileStream_lock , PaddedMutex , max_nonleaf, false, Monitor::_safepoint_check_always); #endif diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 82831a09f03..dc14afa54b6 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -142,6 +142,7 @@ extern Mutex* ThreadHeapSampler_lock; // protects the static data for extern Monitor* ThreadsSMRDelete_lock; // Used by ThreadsSMRSupport to take pressure off the Threads_lock 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* NMTCompilationCostHistory_lock; // guards NMT compilation cost history #if INCLUDE_CDS && INCLUDE_JVMTI extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti #endif diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 49c36d91942..e08a4b13990 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -32,6 +32,7 @@ #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" #include "code/scopeDesc.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileTask.hpp" #include "gc/shared/barrierSet.hpp" @@ -3581,6 +3582,7 @@ CompilerThread::CompilerThread(CompileQueue* queue, // Compiler uses resource area for compilation, let's bias it to mtCompiler resource_area()->bias_to(mtCompiler); + _arena_stat = CompilationMemoryStatistic::enabled() ? new ArenaStatCounter : NULL; #ifndef PRODUCT _ideal_graph_printer = NULL; @@ -3590,6 +3592,7 @@ CompilerThread::CompilerThread(CompileQueue* queue, CompilerThread::~CompilerThread() { // Delete objects which were allocated on heap. delete _counters; + delete _arena_stat; } bool CompilerThread::can_call_java() const { diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 1e0d1245d89..666efa75697 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -72,12 +72,15 @@ class ConcurrentLocksDump; class ParkEvent; class Parker; +class ArenaStatCounter; +class BufferBlob; class ciEnv; class CompileThread; class CompileLog; class CompileTask; class CompileQueue; class CompilerCounters; +class CompilerThread; class vframeArray; class DeoptResourceMark; @@ -430,6 +433,12 @@ class Thread: public ThreadShadow { // Can this thread make Java upcalls virtual bool can_call_java() const { return false; } + // Convenience cast functions + CompilerThread* as_Compiler_thread() const { + assert(is_Compiler_thread(), "Must be compiler thread"); + return (CompilerThread*)this; + } + // Casts virtual WorkerThread* as_Worker_thread() const { return NULL; } @@ -957,8 +966,6 @@ class WatcherThread: public NonJavaThread { }; -class CompilerThread; - typedef void (*ThreadFunction)(JavaThread*, TRAPS); class JavaThread: public Thread { @@ -2196,6 +2203,7 @@ class CompilerThread : public JavaThread { AbstractCompiler* _compiler; TimeStamp _idle_time; + ArenaStatCounter* _arena_stat; public: static CompilerThread* current(); @@ -2215,6 +2223,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; } diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index ebcace1a214..1917cef2541 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -27,6 +27,8 @@ #include "classfile/classLoaderHierarchyDCmd.hpp" #include "classfile/classLoaderStats.hpp" #include "classfile/compactHashtable.hpp" +#include "compiler/compilationMemoryStatistic.hpp" +// #include "compiler/compiler_globals.hpp" #include "compiler/compileBroker.hpp" #include "compiler/directivesParser.hpp" #include "gc/shared/vmGCOperations.hpp" @@ -126,6 +128,7 @@ void DCmdRegistrant::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an @@ -1187,3 +1190,17 @@ void QuickStartDumpDCMD::execute(DCmdSource source, TRAPS) { long ms = TimeHelper::counter_to_millis(duration.value()); output()->print_cr("It took %lu ms to execute Quickstart.dump .", ms); } + +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); +} diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index a0ecc89ef7d..86cdbd4be80 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -929,5 +929,28 @@ class QuickStartDumpDCMD : public DCmd { virtual void execute(DCmdSource source, TRAPS); }; +class CompilationMemoryStatisticDCmd: public DCmdWithParser { +protected: + DCmdArgument _human_readable; + DCmdArgument _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", NULL}; + return p; + } + virtual void execute(DCmdSource source, TRAPS); +}; #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 65770cb2ee9..d388d8b0aff 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -350,6 +350,9 @@ inline size_t byte_size_in_exact_unit(size_t s) { #define HEAP_CHANGE_FORMAT_ARGS(_name_, _prev_used_, _prev_capacity_, _used_, _capacity_) \ (_name_), (_prev_used_) / K, (_prev_capacity_) / K, (_used_) / K, (_capacity_) / K +#define PROPERFMT SIZE_FORMAT "%s" +#define PROPERFMTARGS(s) byte_size_in_proper_unit(s), proper_unit_for_byte_size(s) + //---------------------------------------------------------------------------------------------------- // VM type definitions