Skip to content

Commit 0461d9a

Browse files
committed
8318016: Per-compilation memory ceiling
Reviewed-by: roland, thartmann
1 parent 2a76ad9 commit 0461d9a

17 files changed

+426
-34
lines changed

src/hotspot/share/c1/c1_Compilation.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ int Compilation::compile_java_method() {
397397
PhaseTraceTime timeit(_t_buildIR);
398398
build_hir();
399399
}
400+
CHECK_BAILOUT_(no_frame_size);
400401
if (BailoutAfterHIR) {
401402
BAILOUT_("Bailing out because of -XX:+BailoutAfterHIR", no_frame_size);
402403
}
@@ -446,13 +447,13 @@ void Compilation::install_code(int frame_size) {
446447

447448
void Compilation::compile_method() {
448449

449-
CompilationMemoryStatisticMark cmsm(env()->task()->directive());
450-
451450
{
452451
PhaseTraceTime timeit(_t_setup);
453452

454453
// setup compilation
455454
initialize();
455+
CHECK_BAILOUT();
456+
456457
}
457458

458459
if (!method()->can_be_compiled()) {
@@ -605,6 +606,9 @@ Compilation::Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* metho
605606
_cfg_printer_output = new CFGPrinterOutput(this);
606607
}
607608
#endif
609+
610+
CompilationMemoryStatisticMark cmsm(directive);
611+
608612
compile_method();
609613
if (bailed_out()) {
610614
_env->record_method_not_compilable(bailout_msg());

src/hotspot/share/c1/c1_Compilation.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class Compilation: public StackObj {
8585
bool _has_monitors; // Fastpath monitors detection for Continuations
8686
bool _install_code;
8787
const char* _bailout_msg;
88+
bool _oom;
8889
ExceptionInfoList* _exception_info_list;
8990
ExceptionHandlerTable _exception_handler_table;
9091
ImplicitExceptionTable _implicit_exception_table;
@@ -203,6 +204,10 @@ class Compilation: public StackObj {
203204
}
204205
#endif // PRODUCT
205206

207+
// MemLimit handling
208+
bool oom() const { return _oom; }
209+
void set_oom() { _oom = true; }
210+
206211
// error handling
207212
void bailout(const char* msg);
208213
bool bailed_out() const { return _bailout_msg != nullptr; }

src/hotspot/share/ci/ciEnv.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,10 @@ class ciEnv : StackObj {
319319

320320
// This is true if the compilation is not going to produce code.
321321
// (It is reasonable to retry failed compilations.)
322-
bool failing() { return _failure_reason != nullptr; }
322+
bool failing() const { return _failure_reason != nullptr; }
323323

324324
// Reason this compilation is failing, such as "too many basic blocks".
325-
const char* failure_reason() { return _failure_reason; }
325+
const char* failure_reason() const { return _failure_reason; }
326326

327327
// Return state of appropriate compatibility
328328
int compilable() { return _compilable; }

src/hotspot/share/compiler/compilationMemoryStatistic.cpp

Lines changed: 151 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include "precompiled.hpp"
2727
#include "logging/log.hpp"
2828
#include "logging/logStream.hpp"
29+
#ifdef COMPILER1
30+
#include "c1/c1_Compilation.hpp"
31+
#endif
2932
#include "compiler/abstractCompiler.hpp"
3033
#include "compiler/compilationMemoryStatistic.hpp"
3134
#include "compiler/compilerDirectives.hpp"
@@ -42,24 +45,32 @@
4245
#endif
4346
#include "runtime/mutexLocker.hpp"
4447
#include "runtime/os.hpp"
48+
#include "utilities/debug.hpp"
4549
#include "utilities/globalDefinitions.hpp"
4650
#include "utilities/ostream.hpp"
4751
#include "utilities/quickSort.hpp"
4852
#include "utilities/resourceHash.hpp"
4953

50-
5154
ArenaStatCounter::ArenaStatCounter() :
5255
_current(0), _start(0), _peak(0),
5356
_na(0), _ra(0),
57+
_limit(0), _hit_limit(false),
5458
_na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0)
5559
{}
5660

5761
size_t ArenaStatCounter::peak_since_start() const {
5862
return _peak > _start ? _peak - _start : 0;
5963
}
6064

61-
void ArenaStatCounter::start() {
65+
void ArenaStatCounter::start(size_t limit) {
6266
_peak = _start = _current;
67+
_limit = limit;
68+
_hit_limit = false;
69+
}
70+
71+
void ArenaStatCounter::end(){
72+
_limit = 0;
73+
_hit_limit = false;
6374
}
6475

6576
void ArenaStatCounter::update_c2_node_count() {
@@ -104,6 +115,10 @@ bool ArenaStatCounter::account(ssize_t delta, int tag) {
104115
_ra_at_peak = _ra;
105116
update_c2_node_count();
106117
rc = true;
118+
// Did we hit the memory limit?
119+
if (!_hit_limit && _limit > 0 && peak_since_start() > _limit) {
120+
_hit_limit = true;
121+
}
107122
}
108123
return rc;
109124
}
@@ -125,7 +140,8 @@ class FullMethodName {
125140

126141
public:
127142

128-
FullMethodName(Symbol* k, Symbol* m, Symbol* s) : _k(k), _m(m), _s(s) {}
143+
FullMethodName(const Method* m) :
144+
_k(m->klass_name()), _m(m->name()), _s(m->signature()) {};
129145
FullMethodName(const FullMethodName& o) : _k(o._k), _m(o._m), _s(o._s) {}
130146

131147
void make_permanent() {
@@ -173,13 +189,15 @@ class MemStatEntry : public CHeapObj<mtInternal> {
173189
size_t _na_at_peak;
174190
size_t _ra_at_peak;
175191
unsigned _live_nodes_at_peak;
192+
const char* _result;
176193

177194
public:
178195

179196
MemStatEntry(FullMethodName method)
180197
: _method(method), _comptype(compiler_c1),
181198
_time(0), _num_recomp(0), _thread(nullptr),
182-
_total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) {
199+
_total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0),
200+
_result(nullptr) {
183201
}
184202

185203
void set_comptype(CompilerType comptype) { _comptype = comptype; }
@@ -192,22 +210,25 @@ class MemStatEntry : public CHeapObj<mtInternal> {
192210
void set_ra_at_peak(size_t n) { _ra_at_peak = n; }
193211
void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; }
194212

213+
void set_result(const char* s) { _result = s; }
214+
195215
size_t total() const { return _total; }
196216

197217
static void print_legend(outputStream* st) {
198218
st->print_cr("Legend:");
199219
st->print_cr(" total : memory allocated via arenas while compiling");
200220
st->print_cr(" NA : ...how much in node arenas (if c2)");
201221
st->print_cr(" RA : ...how much in resource areas");
202-
st->print_cr(" #nodes : ...how many nodes (if c2)");
222+
st->print_cr(" result : Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed");
223+
st->print_cr(" #nodes : ...how many nodes (c2 only)");
203224
st->print_cr(" time : time of last compilation (sec)");
204225
st->print_cr(" type : compiler type");
205226
st->print_cr(" #rc : how often recompiled");
206227
st->print_cr(" thread : compiler thread");
207228
}
208229

209230
static void print_header(outputStream* st) {
210-
st->print_cr("total NA RA #nodes time type #rc thread method");
231+
st->print_cr("total NA RA result #nodes time type #rc thread method");
211232
}
212233

213234
void print_on(outputStream* st, bool human_readable) const {
@@ -237,6 +258,10 @@ class MemStatEntry : public CHeapObj<mtInternal> {
237258
}
238259
col += 10; st->fill_to(col);
239260

261+
// result?
262+
st->print("%s ", _result ? _result : "");
263+
col += 8; st->fill_to(col);
264+
240265
// Number of Nodes when memory peaked
241266
st->print("%u ", _live_nodes_at_peak);
242267
col += 8; st->fill_to(col);
@@ -281,7 +306,7 @@ class MemStatTable :
281306

282307
void add(const FullMethodName& fmn, CompilerType comptype,
283308
size_t total, size_t na_at_peak, size_t ra_at_peak,
284-
unsigned live_nodes_at_peak) {
309+
unsigned live_nodes_at_peak, const char* result) {
285310
assert_lock_strong(NMTCompilationCostHistory_lock);
286311

287312
MemStatEntry** pe = get(fmn);
@@ -302,6 +327,7 @@ class MemStatTable :
302327
e->set_na_at_peak(na_at_peak);
303328
e->set_ra_at_peak(ra_at_peak);
304329
e->set_live_nodes_at_peak(live_nodes_at_peak);
330+
e->set_result(result);
305331
}
306332

307333
// Returns a C-heap-allocated SortMe array containing all entries from the table,
@@ -341,20 +367,21 @@ void CompilationMemoryStatistic::initialize() {
341367
log_info(compilation, alloc)("Compilation memory statistic enabled");
342368
}
343369

344-
void CompilationMemoryStatistic::on_start_compilation() {
370+
void CompilationMemoryStatistic::on_start_compilation(const DirectiveSet* directive) {
345371
assert(enabled(), "Not enabled?");
346-
Thread::current()->as_Compiler_thread()->arena_stat()->start();
372+
const size_t limit = directive->mem_limit();
373+
Thread::current()->as_Compiler_thread()->arena_stat()->start(limit);
347374
}
348375

349376
void CompilationMemoryStatistic::on_end_compilation() {
350377
assert(enabled(), "Not enabled?");
351378
ResourceMark rm;
352379
CompilerThread* const th = Thread::current()->as_Compiler_thread();
353-
const ArenaStatCounter* const arena_stat = th->arena_stat();
380+
ArenaStatCounter* const arena_stat = th->arena_stat();
354381
const CompilerType ct = th->task()->compiler()->type();
355382

356383
const Method* const m = th->task()->method();
357-
FullMethodName fmn(m->klass_name(), m->name(), m->signature());
384+
FullMethodName fmn(m);
358385
fmn.make_permanent();
359386

360387
const DirectiveSet* directive = th->task()->directive();
@@ -368,6 +395,20 @@ void CompilationMemoryStatistic::on_end_compilation() {
368395
arena_stat->print_on(tty);
369396
tty->cr();
370397
}
398+
399+
// Store result
400+
// For this to work, we must call on_end_compilation() at a point where
401+
// Compile|Compilation already handed over the failure string to ciEnv,
402+
// but ciEnv must still be alive.
403+
const char* result = "ok"; // ok
404+
const ciEnv* const env = th->env();
405+
if (env) {
406+
const char* const failure_reason = env->failure_reason();
407+
if (failure_reason != nullptr) {
408+
result = (failure_reason == failure_reason_memlimit()) ? "oom" : "err";
409+
}
410+
}
411+
371412
{
372413
MutexLocker ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag);
373414
assert(_the_table != nullptr, "not initialized");
@@ -376,14 +417,105 @@ void CompilationMemoryStatistic::on_end_compilation() {
376417
arena_stat->peak_since_start(), // total
377418
arena_stat->na_at_peak(),
378419
arena_stat->ra_at_peak(),
379-
arena_stat->live_nodes_at_peak());
420+
arena_stat->live_nodes_at_peak(),
421+
result);
422+
}
423+
424+
arena_stat->end(); // reset things
425+
}
426+
427+
static void inform_compilation_about_oom(CompilerType ct) {
428+
// Inform C1 or C2 that an OOM happened. They will take delayed action
429+
// and abort the compilation in progress. Note that this is not instantaneous,
430+
// since the compiler has to actively bailout, which may take a while, during
431+
// which memory usage may rise further.
432+
//
433+
// The mechanism differs slightly between C1 and C2:
434+
// - With C1, we directly set the bailout string, which will cause C1 to
435+
// bailout at the typical BAILOUT places.
436+
// - With C2, the corresponding mechanism would be the failure string; but
437+
// bailout paths in C2 are not complete and therefore it is dangerous to
438+
// set the failure string at - for C2 - seemingly random places. Instead,
439+
// upon OOM C2 sets the failure string next time it checks the node limit.
440+
if (ciEnv::current() != nullptr) {
441+
void* compiler_data = ciEnv::current()->compiler_data();
442+
#ifdef COMPILER1
443+
if (ct == compiler_c1) {
444+
Compilation* C = static_cast<Compilation*>(compiler_data);
445+
if (C != nullptr) {
446+
C->bailout(CompilationMemoryStatistic::failure_reason_memlimit());
447+
C->set_oom();
448+
}
449+
}
450+
#endif
451+
#ifdef COMPILER2
452+
if (ct == compiler_c2) {
453+
Compile* C = static_cast<Compile*>(compiler_data);
454+
if (C != nullptr) {
455+
C->set_oom();
456+
}
457+
}
458+
#endif // COMPILER2
380459
}
381460
}
382461

383462
void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* arena) {
384463
assert(enabled(), "Not enabled?");
385464
CompilerThread* const th = Thread::current()->as_Compiler_thread();
386-
th->arena_stat()->account(diff, (int)arena->get_tag());
465+
466+
ArenaStatCounter* const arena_stat = th->arena_stat();
467+
bool hit_limit_before = arena_stat->hit_limit();
468+
469+
if (arena_stat->account(diff, (int)arena->get_tag())) { // new peak?
470+
471+
// Limit handling
472+
if (arena_stat->hit_limit()) {
473+
474+
char name[1024] = "";
475+
bool print = false;
476+
bool crash = false;
477+
CompilerType ct = compiler_none;
478+
479+
// get some more info
480+
const CompileTask* task = th->task();
481+
if (task != nullptr) {
482+
ct = task->compiler()->type();
483+
const DirectiveSet* directive = task->directive();
484+
print = directive->should_print_memstat();
485+
crash = directive->should_crash_at_mem_limit();
486+
const Method* m = th->task()->method();
487+
if (m != nullptr) {
488+
FullMethodName(m).as_C_string(name, sizeof(name));
489+
}
490+
}
491+
492+
char message[1024] = "";
493+
494+
// build up message if we need it later
495+
if (print || crash) {
496+
stringStream ss(message, sizeof(message));
497+
if (ct != compiler_none && name[0] != '\0') {
498+
ss.print("%s %s: ", compilertype2name(ct), name);
499+
}
500+
ss.print("Hit MemLimit %s (limit: %zu now: %zu)",
501+
(hit_limit_before ? "again" : ""),
502+
arena_stat->limit(), arena_stat->peak_since_start());
503+
}
504+
505+
// log if needed
506+
if (print) {
507+
tty->print_raw(message);
508+
tty->cr();
509+
}
510+
511+
// Crash out if needed
512+
if (crash) {
513+
report_fatal(OOM_HOTSPOT_ARENA, __FILE__, __LINE__, "%s", message);
514+
} else {
515+
inform_compilation_about_oom(ct);
516+
}
517+
}
518+
}
387519
}
388520

389521
static inline ssize_t diff_entries_by_size(const MemStatEntry* e1, const MemStatEntry* e2) {
@@ -438,10 +570,15 @@ void CompilationMemoryStatistic::print_all_by_size(outputStream* st, bool human_
438570
FREE_C_HEAP_ARRAY(Entry, filtered);
439571
}
440572

573+
const char* CompilationMemoryStatistic::failure_reason_memlimit() {
574+
static const char* const s = "hit memory limit while compiling";
575+
return s;
576+
}
577+
441578
CompilationMemoryStatisticMark::CompilationMemoryStatisticMark(const DirectiveSet* directive)
442579
: _active(directive->should_collect_memstat()) {
443580
if (_active) {
444-
CompilationMemoryStatistic::on_start_compilation();
581+
CompilationMemoryStatistic::on_start_compilation(directive);
445582
}
446583
}
447584
CompilationMemoryStatisticMark::~CompilationMemoryStatisticMark() {

0 commit comments

Comments
 (0)