mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-25 05:45:11 +02:00
8040798
: compiler/startup/SmallCodeCacheStartup.java timed out in RT_Baseline
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
This commit is contained in:
parent
3fddcd2712
commit
dab05a3040
4 changed files with 171 additions and 149 deletions
|
@ -150,9 +150,8 @@ int CompileBroker::_sum_nmethod_code_size = 0;
|
|||
|
||||
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 @@ CompileTaskWrapper::~CompileTaskWrapper() {
|
|||
|
||||
// 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 @@ void CompileTask::set_code(nmethod* nm) {
|
|||
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 @@ void CompileTask::log_task_done(CompileLog* log) {
|
|||
|
||||
|
||||
|
||||
// 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 @@ void CompileQueue::add(CompileTask* task) {
|
|||
// 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 @@ void CompileQueue::add(CompileTask* task) {
|
|||
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 @@ void CompileQueue::mark_on_stack() {
|
|||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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 @@ void CompileBroker::compilation_init() {
|
|||
_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 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler
|
|||
#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 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler
|
|||
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 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler
|
|||
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 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler
|
|||
}
|
||||
|
||||
|
||||
// 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 @@ void CompileBroker::compile_method_base(methodHandle method,
|
|||
const char* comment,
|
||||
Thread* thread) {
|
||||
// do nothing if compiler thread(s) is not available
|
||||
if (!_initialized ) {
|
||||
if (!_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1078,7 +1124,7 @@ void CompileBroker::compile_method_base(methodHandle method,
|
|||
|
||||
// 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 @@ void CompileBroker::compile_method_base(methodHandle method,
|
|||
// 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 @@ void CompileBroker::compile_method_base(methodHandle method,
|
|||
}
|
||||
|
||||
// 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 @@ bool CompileBroker::compilation_is_complete(methodHandle method,
|
|||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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 @@ int CompileBroker::assign_compile_id(methodHandle method, int osr_bci) {
|
|||
#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 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue,
|
|||
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 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue,
|
|||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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 @@ bool CompileBroker::init_compiler_runtime() {
|
|||
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 @@ bool CompileBroker::init_compiler_runtime() {
|
|||
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 @@ void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerTh
|
|||
// 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue