diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 07d8ec41598..4cc2043656e 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -51,29 +51,36 @@ #include "utilities/quickSort.hpp" #include "utilities/resourceHash.hpp" -ArenaStatCounter::ArenaStatCounter() : - _current(0), _start(0), _peak(0), - _na(0), _ra(0), - _limit(0), _hit_limit(false), _limit_in_process(false), - _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) -{} +ArenaStatCounter::ArenaStatCounter() { + reset(); +} -size_t ArenaStatCounter::peak_since_start() const { - return _peak > _start ? _peak - _start : 0; +void ArenaStatCounter::reset() { + _current = 0; + _peak = 0; + _current_by_tag.clear(); + _peak_by_tag.clear(); + _limit = 0; + _hit_limit = false; + _limit_in_process = false; + _live_nodes_at_peak = 0; + _active = false; } void ArenaStatCounter::start(size_t limit) { - _peak = _start = _current; + reset(); + _active = true; _limit = limit; - _hit_limit = false; } -void ArenaStatCounter::end(){ +void ArenaStatCounter::end() { _limit = 0; _hit_limit = false; + _active = false; } void ArenaStatCounter::update_c2_node_count() { + assert(_active, "compilaton has not yet started"); #ifdef COMPILER2 CompilerThread* const th = Thread::current()->as_Compiler_thread(); const CompileTask* const task = th->task(); @@ -90,33 +97,27 @@ void ArenaStatCounter::update_c2_node_count() { // Account an arena allocation or de-allocation. bool ArenaStatCounter::account(ssize_t delta, int tag) { + assert(_active, "compilaton has not yet started"); 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); + "Negative overflow (d=%zd %zu %zu)", delta, _current, _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; - }; + _current_by_tag.add(tag, delta); // 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; + assert(delta > 0, "Sanity (%zu %zu)", _current, _peak); update_c2_node_count(); + _peak_by_tag = _current_by_tag; rc = true; // Did we hit the memory limit? - if (!_hit_limit && _limit > 0 && peak_since_start() > _limit) { + if (!_hit_limit && _limit > 0 && _peak > _limit) { _hit_limit = true; } } @@ -124,9 +125,15 @@ bool ArenaStatCounter::account(ssize_t delta, int tag) { } void ArenaStatCounter::print_on(outputStream* st) const { - st->print("%zu [na %zu ra %zu]", peak_since_start(), _na_at_peak, _ra_at_peak); + st->print("%zu [", _peak); + for (int tag = 0; tag < _peak_by_tag.element_count(); tag++) { + if (_peak_by_tag.counter(tag) > 0) { + st->print("%s %zu ", _peak_by_tag.tag_name(tag), _peak_by_tag.counter(tag)); + } + } + st->print("]"); #ifdef ASSERT - st->print(" (%zu->%zu->%zu)", _start, _peak, _current); + st->print(" (%zu->%zu)", _peak, _current); #endif } @@ -186,10 +193,8 @@ class MemStatEntry : public CHeapObj<mtInternal> { // peak usage, bytes, over all arenas size_t _total; - // usage in node arena when total peaked - size_t _na_at_peak; - // usage in resource area when total peaked - size_t _ra_at_peak; + // usage per arena tag when total peaked + ArenaCountersByTag _peak_by_tag; // number of nodes (c2 only) when total peaked unsigned _live_nodes_at_peak; const char* _result; @@ -199,8 +204,9 @@ public: MemStatEntry(FullMethodName method) : _method(method), _comptype(compiler_c1), _time(0), _num_recomp(0), _thread(nullptr), _limit(0), - _total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0), + _total(0), _live_nodes_at_peak(0), _result(nullptr) { + _peak_by_tag.clear(); } void set_comptype(CompilerType comptype) { _comptype = comptype; } @@ -210,8 +216,7 @@ public: 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_peak_by_tag(ArenaCountersByTag peak_by_tag) { _peak_by_tag = peak_by_tag; } void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; } void set_result(const char* s) { _result = s; } @@ -219,21 +224,34 @@ public: size_t total() const { return _total; } static void print_legend(outputStream* st) { +#define LEGEND_KEY_FMT "%11s" 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(" result : Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); - st->print_cr(" #nodes : ...how many nodes (c2 only)"); - st->print_cr(" limit : memory limit, if set"); - 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"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "total", "memory allocated via arenas while compiling"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print_cr(" " LEGEND_KEY_FMT ": %s", Arena::tag_name[tag], Arena::tag_desc[tag]); + } + st->print_cr(" " LEGEND_KEY_FMT ": %s", "result", "Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#nodes", "...how many nodes (c2 only)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "limit", "memory limit, if set"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "time", "time taken for last compilation (sec)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "type", "compiler type"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#rc", "how often recompiled"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "thread", "compiler thread"); +#undef LEGEND_KEY_FMT } static void print_header(outputStream* st) { - st->print_cr("total NA RA result #nodes limit time type #rc thread method"); +#define SIZE_FMT "%-10s" + st->print(SIZE_FMT, "total"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print(SIZE_FMT, Arena::tag_name[tag]); + } +#define HDR_FMT1 "%-8s%-8s%-8s%-8s" +#define HDR_FMT2 "%-6s%-4s%-19s%s" + + st->print(HDR_FMT1, "result", "#nodes", "limit", "time"); + st->print(HDR_FMT2, "type", "#rc", "thread", "method"); + st->print_cr(""); } void print_on(outputStream* st, bool human_readable) const { @@ -247,21 +265,14 @@ public: } col += 10; st->fill_to(col); - // NA - if (human_readable) { - st->print(PROPERFMT " ", PROPERFMTARGS(_na_at_peak)); - } else { - st->print("%zu ", _na_at_peak); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_peak_by_tag.counter(tag))); + } else { + st->print("%zu ", _peak_by_tag.counter(tag)); + } + col += 10; st->fill_to(col); } - 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); // result? st->print("%s ", _result ? _result : ""); @@ -296,7 +307,7 @@ public: col += 4; st->fill_to(col); // Thread - st->print(PTR_FORMAT " ", p2i(_thread)); + st->print(PTR_FORMAT " ", p2i(_thread)); // MethodName char buf[1024]; @@ -341,7 +352,7 @@ class MemStatTable : public: void add(const FullMethodName& fmn, CompilerType comptype, - size_t total, size_t na_at_peak, size_t ra_at_peak, + size_t total, ArenaCountersByTag peak_by_tag, unsigned live_nodes_at_peak, size_t limit, const char* result) { assert_lock_strong(NMTCompilationCostHistory_lock); MemStatTableKey key(fmn, comptype); @@ -360,8 +371,7 @@ public: 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_peak_by_tag(peak_by_tag); e->set_live_nodes_at_peak(live_nodes_at_peak); e->set_limit(limit); e->set_result(result); @@ -427,7 +437,7 @@ void CompilationMemoryStatistic::on_end_compilation() { const bool print = directive->should_print_memstat(); // Store memory used in task, for later processing by JFR - task->set_arena_bytes(arena_stat->peak_since_start()); + task->set_arena_bytes(arena_stat->peak()); // Store result // For this to work, we must call on_end_compilation() at a point where @@ -447,9 +457,8 @@ void CompilationMemoryStatistic::on_end_compilation() { 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->peak(), // total + arena_stat->peak_by_tag(), arena_stat->live_nodes_at_peak(), arena_stat->limit(), result); @@ -511,7 +520,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren bool hit_limit_before = arena_stat->hit_limit(); - if (arena_stat->account(diff, (int)arena->get_tag())) { // new peak? + if (arena_stat->is_active() && arena_stat->account(diff, (int)arena->get_tag())) { // new peak? // Limit handling if (arena_stat->hit_limit()) { @@ -545,7 +554,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren } ss.print("Hit MemLimit %s(limit: %zu now: %zu)", (hit_limit_before ? "again " : ""), - arena_stat->limit(), arena_stat->peak_since_start()); + arena_stat->limit(), arena_stat->peak()); } // log if needed diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp index d7cccad17fd..d669cc14722 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp @@ -29,48 +29,70 @@ #include "compiler/compilerDefinitions.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "memory/arena.hpp" #include "utilities/globalDefinitions.hpp" class outputStream; class Symbol; class DirectiveSet; -// Counters for allocations from one arena +// Helper class to wrap the array of arena tags for easier processing +class ArenaCountersByTag { +private: + size_t _counter[Arena::tag_count()]; + +public: + int element_count() const { return Arena::tag_count(); } + const char* tag_name(int tag) const { return Arena::tag_name[tag]; } + + size_t counter(int tag) const { + assert(tag < element_count(), "invalid tag %d", tag); + return _counter[tag]; + } + + void add(int tag, size_t value) { + assert(tag < element_count(), "invalid tag %d", tag); + _counter[tag] += value; + } + + void clear() { + memset(_counter, 0, sizeof(size_t) * element_count()); + } +}; + +// Counters for allocations from arenas during compilation 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; + // Current bytes used by arenas per tag + ArenaCountersByTag _current_by_tag; + // Peak composition: + ArenaCountersByTag _peak_by_tag; // MemLimit handling size_t _limit; bool _hit_limit; bool _limit_in_process; - // 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; + // When to start accounting + bool _active; + // Number of live nodes when total peaked (c2 only) unsigned _live_nodes_at_peak; void update_c2_node_count(); + void reset(); + public: ArenaStatCounter(); // Size of peak since last compilation - size_t peak_since_start() const; + size_t peak() const { return _peak; } // Peak details - size_t na_at_peak() const { return _na_at_peak; } - size_t ra_at_peak() const { return _ra_at_peak; } + ArenaCountersByTag peak_by_tag() const { return _peak_by_tag; } unsigned live_nodes_at_peak() const { return _live_nodes_at_peak; } // Mark the start and end of a compilation. @@ -89,7 +111,7 @@ public: bool hit_limit() const { return _hit_limit; } bool limit_in_process() const { return _limit_in_process; } void set_limit_in_process(bool v) { _limit_in_process = v; } - + bool is_active() const { return _active; } }; class CompilationMemoryStatistic : public AllStatic { diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index 0399c6922e3..435a8cfdd58 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -44,6 +44,19 @@ STATIC_ASSERT(is_aligned((int)Chunk::init_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::medium_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::size, ARENA_AMALLOC_ALIGNMENT)); + +const char* Arena::tag_name[] = { +#define ARENA_TAG_STRING(name, str, desc) XSTR(name), + DO_ARENA_TAG(ARENA_TAG_STRING) +#undef ARENA_TAG_STRING +}; + +const char* Arena::tag_desc[] = { +#define ARENA_TAG_DESC(name, str, desc) XSTR(desc), + DO_ARENA_TAG(ARENA_TAG_DESC) +#undef ARENA_TAG_DESC +}; + // MT-safe pool of same-sized chunks to reduce malloc/free thrashing // NB: not using Mutex because pools are used before Threads are initialized class ChunkPool { diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 1ca8a787825..1f3c5eb4e8b 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -83,17 +83,29 @@ public: bool contains(char* p) const { return bottom() <= p && p <= top(); } }; +#define DO_ARENA_TAG(FN) \ + FN(other, Others, Other arenas) \ + FN(ra, RA, Resource areas) \ + FN(ha, HA, Handle area) \ + FN(node, NA, Node arena) \ + // Fast allocation of memory class Arena : public CHeapObjBase { public: - - enum class Tag : uint8_t { - tag_other = 0, - tag_ra, // resource area - tag_ha, // handle area - tag_node // C2 Node arena + enum class Tag: uint8_t { +#define ARENA_TAG_ENUM(name, str, desc) tag_##name, + DO_ARENA_TAG(ARENA_TAG_ENUM) +#undef ARENA_TAG_ENUM + tag_count }; + constexpr static int tag_count() { + return static_cast<int>(Tag::tag_count); + } + + static const char* tag_name[static_cast<int>(Arena::Tag::tag_count)]; + static const char* tag_desc[static_cast<int>(Arena::Tag::tag_count)]; + private: const MEMFLAGS _flags; // Memory tracking flags const Tag _tag; diff --git a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java index c2689d88372..2dbf46fd040 100644 --- a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java +++ b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java @@ -77,16 +77,17 @@ public class CompileCommandPrintMemStat { // Should see final report // Looks like this: - // total NA RA result #nodes limit time type #rc thread method - // 2149912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); + // total Others RA HA NA result #nodes limit time type #rc thread method + // 523648 32728 490920 0 0 ok - - 0.250 c1 1 0x00007f4ec00d4ac0 java/lang/Class::descriptorString(()Ljava/lang/String;) // or - // 537784 98184 208536 ok 267 - 0.096 c2 1 0x0000000153019c00 jdk/internal/classfile/impl/BufWriterImpl::writeU1((I)V) 4521912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) + oa.shouldMatch("total.*method"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); // In debug builds, we have a default memory limit enabled. That implies MemStat. Therefore we // expect to see all methods, not just the one we specified on the command line. if (Platform.isDebugBuild()) { - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); } else { oa.shouldNotMatch(".*" + expectedNameExcl + ".*"); } diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java index 7d4a38db47b..02bc77263d8 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java @@ -47,9 +47,9 @@ public class CompilerMemoryStatisticTest { out.shouldHaveExitValue(0); // Looks like this: - // total NA RA result #nodes time type #rc thread method - // 211488 66440 77624 ok 13 0.057 c2 2 0x00007fb49428db70 compiler/print/CompileCommandPrintMemStat$TestMain::method1(()V) + // total Others RA HA NA result #nodes limit time type #rc thread method + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) out.shouldMatch("total.*method"); - out.shouldMatch("\\d+ +\\d+ +\\d+ +\\S+ +\\d+.*java.*\\(.*\\)"); + out.shouldMatch("\\d+ +(\\d+ +){4}\\S+ +\\d+.*java.*\\(.*\\)"); } }