8040798: compiler/startup/SmallCodeCacheStartup.java timed out in RT_Baseline
Summary: Fixes broken memory freeing of compile queue tasks and makes sure that blocking compiles do not hang the VM if compilation gets disabled due to a full code cache.
Reviewed-by: kvn, iveresov
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Mon Apr 28 16:05:12 2014 -0700
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Tue Apr 29 07:59:22 2014 +0200
@@ -150,9 +150,8 @@
long CompileBroker::_peak_compilation_time = 0;
-CompileQueue* CompileBroker::_c2_method_queue = NULL;
-CompileQueue* CompileBroker::_c1_method_queue = NULL;
-CompileTask* CompileBroker::_task_free_list = NULL;
+CompileQueue* CompileBroker::_c2_compile_queue = NULL;
+CompileQueue* CompileBroker::_c1_compile_queue = NULL;
GrowableArray<CompilerThread*>* CompileBroker::_compiler_threads = NULL;
@@ -220,13 +219,56 @@
// By convention, the compiling thread is responsible for
// recycling a non-blocking CompileTask.
- CompileBroker::free_task(task);
+ CompileTask::free(task);
}
}
-// ------------------------------------------------------------------
-// CompileTask::initialize
+CompileTask* CompileTask::_task_free_list = NULL;
+#ifdef ASSERT
+int CompileTask::_num_allocated_tasks = 0;
+#endif
+/**
+ * Allocate a CompileTask, from the free list if possible.
+ */
+CompileTask* CompileTask::allocate() {
+ MutexLocker locker(CompileTaskAlloc_lock);
+ CompileTask* task = NULL;
+
+ if (_task_free_list != NULL) {
+ task = _task_free_list;
+ _task_free_list = task->next();
+ task->set_next(NULL);
+ } else {
+ task = new CompileTask();
+ DEBUG_ONLY(_num_allocated_tasks++;)
+ assert (_num_allocated_tasks < 10000, "Leaking compilation tasks?");
+ task->set_next(NULL);
+ task->set_is_free(true);
+ }
+ assert(task->is_free(), "Task must be free.");
+ task->set_is_free(false);
+ return task;
+}
+
+
+/**
+ * Add a task to the free list.
+ */
+void CompileTask::free(CompileTask* task) {
+ MutexLocker locker(CompileTaskAlloc_lock);
+ if (!task->is_free()) {
+ task->set_code(NULL);
+ assert(!task->lock()->is_locked(), "Should not be locked when freed");
+ JNIHandles::destroy_global(task->_method_holder);
+ JNIHandles::destroy_global(task->_hot_method_holder);
+
+ task->set_is_free(true);
+ task->set_next(_task_free_list);
+ _task_free_list = task;
+ }
+}
+
void CompileTask::initialize(int compile_id,
methodHandle method,
int osr_bci,
@@ -284,15 +326,6 @@
if (nm == NULL) _code_handle = NULL; // drop the handle also
}
-// ------------------------------------------------------------------
-// CompileTask::free
-void CompileTask::free() {
- set_code(NULL);
- assert(!_lock->is_locked(), "Should not be locked when freed");
- JNIHandles::destroy_global(_method_holder);
- JNIHandles::destroy_global(_hot_method_holder);
-}
-
void CompileTask::mark_on_stack() {
// Mark these methods as something redefine classes cannot remove.
@@ -555,9 +588,12 @@
-// Add a CompileTask to a CompileQueue
+/**
+ * Add a CompileTask to a CompileQueue
+ */
void CompileQueue::add(CompileTask* task) {
assert(lock()->owned_by_self(), "must own lock");
+ assert(!CompileBroker::is_compilation_disabled_forever(), "Do not add task if compilation is turned off forever");
task->set_next(NULL);
task->set_prev(NULL);
@@ -579,9 +615,7 @@
// Mark the method as being in the compile queue.
task->method()->set_queued_for_compilation();
- if (CIPrintCompileQueue) {
- print();
- }
+ NOT_PRODUCT(print();)
if (LogCompilation && xtty != NULL) {
task->log_task_queued();
@@ -591,14 +625,19 @@
lock()->notify_all();
}
-void CompileQueue::delete_all() {
- assert(lock()->owned_by_self(), "must own lock");
+void CompileQueue::free_all() {
+ MutexLocker mu(lock());
if (_first != NULL) {
for (CompileTask* task = _first; task != NULL; task = task->next()) {
- delete task;
+ // Wake up thread that blocks on the compile task.
+ task->lock()->notify();
+ // Puts task back on the freelist.
+ CompileTask::free(task);
}
_first = NULL;
}
+ // Wake up all threads that block on the queue.
+ lock()->notify_all();
}
// ------------------------------------------------------------------
@@ -691,18 +730,24 @@
}
}
-// ------------------------------------------------------------------
-// CompileQueue::print
+#ifndef PRODUCT
+/**
+ * Print entire compilation queue.
+ */
void CompileQueue::print() {
- tty->print_cr("Contents of %s", name());
- tty->print_cr("----------------------");
- CompileTask* task = _first;
- while (task != NULL) {
- task->print_line();
- task = task->next();
+ if (CIPrintCompileQueue) {
+ ttyLocker ttyl;
+ tty->print_cr("Contents of %s", name());
+ tty->print_cr("----------------------");
+ CompileTask* task = _first;
+ while (task != NULL) {
+ task->print_line();
+ task = task->next();
+ }
+ tty->print_cr("----------------------");
}
- tty->print_cr("----------------------");
}
+#endif // PRODUCT
CompilerCounters::CompilerCounters(const char* thread_name, int instance, TRAPS) {
@@ -775,9 +820,6 @@
_compilers[1] = new SharkCompiler();
#endif // SHARK
- // Initialize the CompileTask free list
- _task_free_list = NULL;
-
// Start the CompilerThreads
init_compiler_threads(c1_count, c2_count);
// totalTime performance counter is always created as it is required
@@ -970,11 +1012,11 @@
#endif // !ZERO && !SHARK
// Initialize the compilation queue
if (c2_compiler_count > 0) {
- _c2_method_queue = new CompileQueue("C2MethodQueue", MethodCompileQueue_lock);
+ _c2_compile_queue = new CompileQueue("C2 CompileQueue", MethodCompileQueue_lock);
_compilers[1]->set_num_compiler_threads(c2_compiler_count);
}
if (c1_compiler_count > 0) {
- _c1_method_queue = new CompileQueue("C1MethodQueue", MethodCompileQueue_lock);
+ _c1_compile_queue = new CompileQueue("C1 CompileQueue", MethodCompileQueue_lock);
_compilers[0]->set_num_compiler_threads(c1_compiler_count);
}
@@ -989,7 +1031,7 @@
sprintf(name_buffer, "C2 CompilerThread%d", i);
CompilerCounters* counters = new CompilerCounters("compilerThread", i, CHECK);
// Shark and C2
- CompilerThread* new_thread = make_compiler_thread(name_buffer, _c2_method_queue, counters, _compilers[1], CHECK);
+ CompilerThread* new_thread = make_compiler_thread(name_buffer, _c2_compile_queue, counters, _compilers[1], CHECK);
_compiler_threads->append(new_thread);
}
@@ -998,7 +1040,7 @@
sprintf(name_buffer, "C1 CompilerThread%d", i);
CompilerCounters* counters = new CompilerCounters("compilerThread", i, CHECK);
// C1
- CompilerThread* new_thread = make_compiler_thread(name_buffer, _c1_method_queue, counters, _compilers[0], CHECK);
+ CompilerThread* new_thread = make_compiler_thread(name_buffer, _c1_compile_queue, counters, _compilers[0], CHECK);
_compiler_threads->append(new_thread);
}
@@ -1008,14 +1050,18 @@
}
-// Set the methods on the stack as on_stack so that redefine classes doesn't
-// reclaim them
+/**
+ * Set the methods on the stack as on_stack so that redefine classes doesn't
+ * reclaim them
+ */
void CompileBroker::mark_on_stack() {
- if (_c2_method_queue != NULL) {
- _c2_method_queue->mark_on_stack();
+ if (_c2_compile_queue != NULL) {
+ MutexLocker locker(_c2_compile_queue->lock());
+ _c2_compile_queue->mark_on_stack();
}
- if (_c1_method_queue != NULL) {
- _c1_method_queue->mark_on_stack();
+ if (_c1_compile_queue != NULL) {
+ MutexLocker locker(_c1_compile_queue->lock());
+ _c1_compile_queue->mark_on_stack();
}
}
@@ -1031,7 +1077,7 @@
const char* comment,
Thread* thread) {
// do nothing if compiler thread(s) is not available
- if (!_initialized ) {
+ if (!_initialized) {
return;
}
@@ -1078,7 +1124,7 @@
// If this method is already in the compile queue, then
// we do not block the current thread.
- if (compilation_is_in_queue(method, osr_bci)) {
+ if (compilation_is_in_queue(method)) {
// We may want to decay our counter a bit here to prevent
// multiple denied requests for compilation. This is an
// open compilation policy issue. Note: The other possibility,
@@ -1111,7 +1157,7 @@
// Make sure the method has not slipped into the queues since
// last we checked; note that those checks were "fast bail-outs".
// Here we need to be more careful, see 14012000 below.
- if (compilation_is_in_queue(method, osr_bci)) {
+ if (compilation_is_in_queue(method)) {
return;
}
@@ -1132,7 +1178,7 @@
}
// Should this thread wait for completion of the compile?
- blocking = is_compile_blocking(method, osr_bci);
+ blocking = is_compile_blocking();
// We will enter the compilation in the queue.
// 14012000: Note that this sets the queued_for_compile bits in
@@ -1324,19 +1370,17 @@
}
-// ------------------------------------------------------------------
-// CompileBroker::compilation_is_in_queue
-//
-// See if this compilation is already requested.
-//
-// Implementation note: there is only a single "is in queue" bit
-// for each method. This means that the check below is overly
-// conservative in the sense that an osr compilation in the queue
-// will block a normal compilation from entering the queue (and vice
-// versa). This can be remedied by a full queue search to disambiguate
-// cases. If it is deemed profitible, this may be done.
-bool CompileBroker::compilation_is_in_queue(methodHandle method,
- int osr_bci) {
+/**
+ * See if this compilation is already requested.
+ *
+ * Implementation note: there is only a single "is in queue" bit
+ * for each method. This means that the check below is overly
+ * conservative in the sense that an osr compilation in the queue
+ * will block a normal compilation from entering the queue (and vice
+ * versa). This can be remedied by a full queue search to disambiguate
+ * cases. If it is deemed profitable, this may be done.
+ */
+bool CompileBroker::compilation_is_in_queue(methodHandle method) {
return method->queued_for_compilation();
}
@@ -1416,13 +1460,11 @@
#endif
}
-
-// ------------------------------------------------------------------
-// CompileBroker::is_compile_blocking
-//
-// Should the current thread be blocked until this compilation request
-// has been fulfilled?
-bool CompileBroker::is_compile_blocking(methodHandle method, int osr_bci) {
+/**
+ * Should the current thread block until this compilation request
+ * has been fulfilled?
+ */
+bool CompileBroker::is_compile_blocking() {
assert(!InstanceRefKlass::owns_pending_list_lock(JavaThread::current()), "possible deadlock");
return !BackgroundCompilation;
}
@@ -1450,7 +1492,7 @@
int hot_count,
const char* comment,
bool blocking) {
- CompileTask* new_task = allocate_task();
+ CompileTask* new_task = CompileTask::allocate();
new_task->initialize(compile_id, method, osr_bci, comp_level,
hot_method, hot_count, comment,
blocking);
@@ -1459,75 +1501,52 @@
}
-// ------------------------------------------------------------------
-// CompileBroker::allocate_task
-//
-// Allocate a CompileTask, from the free list if possible.
-CompileTask* CompileBroker::allocate_task() {
- MutexLocker locker(CompileTaskAlloc_lock);
- CompileTask* task = NULL;
- if (_task_free_list != NULL) {
- task = _task_free_list;
- _task_free_list = task->next();
- task->set_next(NULL);
- } else {
- task = new CompileTask();
- task->set_next(NULL);
- }
- return task;
-}
-
-
-// ------------------------------------------------------------------
-// CompileBroker::free_task
-//
-// Add a task to the free list.
-void CompileBroker::free_task(CompileTask* task) {
- MutexLocker locker(CompileTaskAlloc_lock);
- task->free();
- task->set_next(_task_free_list);
- _task_free_list = task;
-}
-
-
-// ------------------------------------------------------------------
-// CompileBroker::wait_for_completion
-//
-// Wait for the given method CompileTask to complete.
+/**
+ * Wait for the compilation task to complete.
+ */
void CompileBroker::wait_for_completion(CompileTask* task) {
if (CIPrintCompileQueue) {
+ ttyLocker ttyl;
tty->print_cr("BLOCKING FOR COMPILE");
}
assert(task->is_blocking(), "can only wait on blocking task");
- JavaThread *thread = JavaThread::current();
+ JavaThread* thread = JavaThread::current();
thread->set_blocked_on_compilation(true);
methodHandle method(thread, task->method());
{
MutexLocker waiter(task->lock(), thread);
- while (!task->is_complete())
+ while (!task->is_complete() && !is_compilation_disabled_forever()) {
task->lock()->wait();
+ }
}
+
+ thread->set_blocked_on_compilation(false);
+ if (is_compilation_disabled_forever()) {
+ CompileTask::free(task);
+ return;
+ }
+
// It is harmless to check this status without the lock, because
// completion is a stable property (until the task object is recycled).
assert(task->is_complete(), "Compilation should have completed");
assert(task->code_handle() == NULL, "must be reset");
- thread->set_blocked_on_compilation(false);
-
// By convention, the waiter is responsible for recycling a
// blocking CompileTask. Since there is only one waiter ever
// waiting on a CompileTask, we know that no one else will
// be using this CompileTask; we can free it.
- free_task(task);
+ CompileTask::free(task);
}
-// Initialize compiler thread(s) + compiler object(s). The postcondition
-// of this function is that the compiler runtimes are initialized and that
-//compiler threads can start compiling.
+/**
+ * Initialize compiler thread(s) + compiler object(s). The postcondition
+ * of this function is that the compiler runtimes are initialized and that
+ * compiler threads can start compiling.
+ */
bool CompileBroker::init_compiler_runtime() {
CompilerThread* thread = CompilerThread::current();
AbstractCompiler* comp = thread->compiler();
@@ -1564,7 +1583,6 @@
disable_compilation_forever();
// If compiler initialization failed, no compiler thread that is specific to a
// particular compiler runtime will ever start to compile methods.
-
shutdown_compiler_runtime(comp, thread);
return false;
}
@@ -1578,9 +1596,11 @@
return true;
}
-// If C1 and/or C2 initialization failed, we shut down all compilation.
-// We do this to keep things simple. This can be changed if it ever turns out to be
-// a problem.
+/**
+ * If C1 and/or C2 initialization failed, we shut down all compilation.
+ * We do this to keep things simple. This can be changed if it ever turns
+ * out to be a problem.
+ */
void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread) {
// Free buffer blob, if allocated
if (thread->get_buffer_blob() != NULL) {
@@ -1592,28 +1612,25 @@
// There are two reasons for shutting down the compiler
// 1) compiler runtime initialization failed
// 2) The code cache is full and the following flag is set: -XX:-UseCodeCacheFlushing
- warning("Shutting down compiler %s (no space to run compilers)", comp->name());
+ warning("%s initialization failed. Shutting down all compilers", comp->name());
// Only one thread per compiler runtime object enters here
// Set state to shut down
comp->set_shut_down();
- MutexLocker mu(MethodCompileQueue_lock, thread);
- CompileQueue* queue;
- if (_c1_method_queue != NULL) {
- _c1_method_queue->delete_all();
- queue = _c1_method_queue;
- _c1_method_queue = NULL;
- delete _c1_method_queue;
+ // Delete all queued compilation tasks to make compiler threads exit faster.
+ if (_c1_compile_queue != NULL) {
+ _c1_compile_queue->free_all();
}
- if (_c2_method_queue != NULL) {
- _c2_method_queue->delete_all();
- queue = _c2_method_queue;
- _c2_method_queue = NULL;
- delete _c2_method_queue;
+ if (_c2_compile_queue != NULL) {
+ _c2_compile_queue->free_all();
}
+ // Set flags so that we continue execution with using interpreter only.
+ UseCompiler = false;
+ UseInterpreter = true;
+
// We could delete compiler runtimes also. However, there are references to
// the compiler runtime(s) (e.g., nmethod::is_compiled_by_c1()) which then
// fail. This can be done later if necessary.
--- a/hotspot/src/share/vm/compiler/compileBroker.hpp Mon Apr 28 16:05:12 2014 -0700
+++ b/hotspot/src/share/vm/compiler/compileBroker.hpp Tue Apr 29 07:59:22 2014 +0200
@@ -40,6 +40,11 @@
friend class VMStructs;
private:
+ static CompileTask* _task_free_list;
+#ifdef ASSERT
+ static int _num_allocated_tasks;
+#endif
+
Monitor* _lock;
uint _compile_id;
Method* _method;
@@ -52,7 +57,7 @@
int _num_inlined_bytecodes;
nmethodLocker* _code_handle; // holder of eventual result
CompileTask* _next, *_prev;
-
+ bool _is_free;
// Fields used for logging why the compilation was initiated:
jlong _time_queued; // in units of os::elapsed_counter()
Method* _hot_method; // which method actually triggered this task
@@ -69,7 +74,8 @@
methodHandle hot_method, int hot_count, const char* comment,
bool is_blocking);
- void free();
+ static CompileTask* allocate();
+ static void free(CompileTask* task);
int compile_id() const { return _compile_id; }
Method* method() const { return _method; }
@@ -98,6 +104,8 @@
void set_next(CompileTask* next) { _next = next; }
CompileTask* prev() const { return _prev; }
void set_prev(CompileTask* prev) { _prev = prev; }
+ bool is_free() const { return _is_free; }
+ void set_is_free(bool val) { _is_free = val; }
private:
static void print_compilation_impl(outputStream* st, Method* method, int compile_id, int comp_level,
@@ -213,8 +221,8 @@
// Redefine Classes support
void mark_on_stack();
- void delete_all();
- void print();
+ void free_all();
+ NOT_PRODUCT (void print();)
~CompileQueue() {
assert (is_empty(), " Compile Queue must be empty");
@@ -267,9 +275,8 @@
static int _last_compile_level;
static char _last_method_compiled[name_buffer_length];
- static CompileQueue* _c2_method_queue;
- static CompileQueue* _c1_method_queue;
- static CompileTask* _task_free_list;
+ static CompileQueue* _c2_compile_queue;
+ static CompileQueue* _c1_compile_queue;
static GrowableArray<CompilerThread*>* _compiler_threads;
@@ -322,7 +329,7 @@
static void init_compiler_threads(int c1_compiler_count, int c2_compiler_count);
static bool compilation_is_complete (methodHandle method, int osr_bci, int comp_level);
static bool compilation_is_prohibited(methodHandle method, int osr_bci, int comp_level);
- static bool is_compile_blocking (methodHandle method, int osr_bci);
+ static bool is_compile_blocking ();
static void preload_classes (methodHandle method, TRAPS);
static CompileTask* create_compile_task(CompileQueue* queue,
@@ -334,8 +341,6 @@
int hot_count,
const char* comment,
bool blocking);
- static CompileTask* allocate_task();
- static void free_task(CompileTask* task);
static void wait_for_completion(CompileTask* task);
static void invoke_compiler_on_method(CompileTask* task);
@@ -353,8 +358,8 @@
const char* comment,
Thread* thread);
static CompileQueue* compile_queue(int comp_level) {
- if (is_c2_compile(comp_level)) return _c2_method_queue;
- if (is_c1_compile(comp_level)) return _c1_method_queue;
+ if (is_c2_compile(comp_level)) return _c2_compile_queue;
+ if (is_c1_compile(comp_level)) return _c1_compile_queue;
return NULL;
}
static bool init_compiler_runtime();
@@ -372,7 +377,7 @@
return NULL;
}
- static bool compilation_is_in_queue(methodHandle method, int osr_bci);
+ static bool compilation_is_in_queue(methodHandle method);
static int queue_size(int comp_level) {
CompileQueue *q = compile_queue(comp_level);
return q != NULL ? q->size() : 0;
--- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Mon Apr 28 16:05:12 2014 -0700
+++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Tue Apr 29 07:59:22 2014 +0200
@@ -449,7 +449,7 @@
if (should_create_mdo(mh(), level)) {
create_mdo(mh, thread);
}
- if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, InvocationEntryBci)) {
+ if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh)) {
CompLevel next_level = call_event(mh(), level);
if (next_level != level) {
compile(mh, InvocationEntryBci, next_level, thread);
@@ -473,7 +473,7 @@
CompLevel next_osr_level = loop_event(imh(), level);
CompLevel max_osr_level = (CompLevel)imh->highest_osr_comp_level();
// At the very least compile the OSR version
- if (!CompileBroker::compilation_is_in_queue(imh, bci) && next_osr_level != level) {
+ if (!CompileBroker::compilation_is_in_queue(imh) && (next_osr_level != level)) {
compile(imh, bci, next_osr_level, thread);
}
@@ -507,7 +507,7 @@
nm->make_not_entrant();
}
}
- if (!CompileBroker::compilation_is_in_queue(mh, InvocationEntryBci)) {
+ if (!CompileBroker::compilation_is_in_queue(mh)) {
// Fix up next_level if necessary to avoid deopts
if (next_level == CompLevel_limited_profile && max_osr_level == CompLevel_full_profile) {
next_level = CompLevel_full_profile;
@@ -519,7 +519,7 @@
} else {
cur_level = comp_level(imh());
next_level = call_event(imh(), cur_level);
- if (!CompileBroker::compilation_is_in_queue(imh, bci) && next_level != cur_level) {
+ if (!CompileBroker::compilation_is_in_queue(imh) && (next_level != cur_level)) {
compile(imh, InvocationEntryBci, next_level, thread);
}
}
--- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Mon Apr 28 16:05:12 2014 -0700
+++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Tue Apr 29 07:59:22 2014 +0200
@@ -235,7 +235,7 @@
if (bci != InvocationEntryBci && mh->is_not_osr_compilable(level)) {
return;
}
- if (!CompileBroker::compilation_is_in_queue(mh, bci)) {
+ if (!CompileBroker::compilation_is_in_queue(mh)) {
if (PrintTieredEvents) {
print_event(COMPILE, mh, mh, bci, level);
}
@@ -374,7 +374,7 @@
// Handle the invocation event.
void SimpleThresholdPolicy::method_invocation_event(methodHandle mh, methodHandle imh,
CompLevel level, nmethod* nm, JavaThread* thread) {
- if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, InvocationEntryBci)) {
+ if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh)) {
CompLevel next_level = call_event(mh(), level);
if (next_level != level) {
compile(mh, InvocationEntryBci, next_level, thread);
@@ -387,7 +387,7 @@
void SimpleThresholdPolicy::method_back_branch_event(methodHandle mh, methodHandle imh,
int bci, CompLevel level, nmethod* nm, JavaThread* thread) {
// If the method is already compiling, quickly bail out.
- if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, bci)) {
+ if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh)) {
// Use loop event as an opportunity to also check there's been
// enough calls.
CompLevel cur_level = comp_level(mh());