diff -r 18fd6d87f16f src/share/vm/ci/ciEnv.cpp --- a/src/share/vm/ci/ciEnv.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/ci/ciEnv.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -108,6 +108,9 @@ _system_dictionary_modification_counter = system_dictionary_modification_counter; _num_inlined_bytecodes = 0; assert(task == NULL || thread->task() == task, "sanity"); + if (task != NULL) { + task->mark_started(os::elapsed_counter()); + } _task = task; _log = NULL; diff -r 18fd6d87f16f src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/compiler/compileBroker.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -231,14 +231,14 @@ CompilerThread* thread = CompilerThread::current(); thread->set_task(task); CompileLog* log = thread->log(); - if (log != NULL) task->log_task_start(log); + if (log != NULL && !task->is_unloaded()) task->log_task_start(log); } CompileTaskWrapper::~CompileTaskWrapper() { CompilerThread* thread = CompilerThread::current(); CompileTask* task = thread->task(); CompileLog* log = thread->log(); - if (log != NULL) task->log_task_done(log); + if (log != NULL && !task->is_unloaded()) task->log_task_done(log); thread->set_task(NULL); task->set_code_handle(NULL); thread->set_env(NULL); @@ -293,8 +293,14 @@ 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); + if ((task->_method_holder != NULL && JNIHandles::is_weak_global_handle(task->_method_holder)) || + (task->_hot_method_holder != NULL && JNIHandles::is_weak_global_handle(task->_hot_method_holder))) { + JNIHandles::destroy_weak_global(task->_method_holder); + JNIHandles::destroy_weak_global(task->_hot_method_holder); + } else { + JNIHandles::destroy_global(task->_method_holder); + JNIHandles::destroy_global(task->_hot_method_holder); + } task->set_is_free(true); task->set_next(_task_free_list); @@ -312,9 +318,10 @@ bool is_blocking) { assert(!_lock->is_locked(), "bad locking"); + Thread* thread = Thread::current(); _compile_id = compile_id; _method = method(); - _method_holder = JNIHandles::make_global(method->method_holder()->klass_holder()); + _method_holder = JNIHandles::make_weak_global(Handle(thread, method->method_holder()->klass_holder())); _osr_bci = osr_bci; _is_blocking = is_blocking; _comp_level = comp_level; @@ -327,19 +334,19 @@ _hot_method = NULL; _hot_method_holder = NULL; _hot_count = hot_count; - _time_queued = 0; // tidy + _time_queued = os::elapsed_counter(); + _time_started = 0; _comment = comment; _failure_reason = NULL; if (LogCompilation) { - _time_queued = os::elapsed_counter(); if (hot_method.not_null()) { if (hot_method == method) { _hot_method = _method; } else { _hot_method = hot_method(); // only add loader or mirror if different from _method_holder - _hot_method_holder = JNIHandles::make_global(hot_method->method_holder()->klass_holder()); + _hot_method_holder = JNIHandles::make_weak_global(Handle(thread, hot_method->method_holder()->klass_holder())); } } } @@ -347,6 +354,24 @@ _next = NULL; } +// Replace weak handles by strong handles to avoid unloading during compilation. +CompileTask* CompileTask::select_for_compilation() { + if (is_unloaded()) { + // Guard against concurrent class unloading + return NULL; + } + Thread* thread = Thread::current(); + // assert(_method->method_holder()->is_loader_alive(), "should be alive"); + Handle method_holder(thread, _method->method_holder()->klass_holder()); + JNIHandles::destroy_weak_global(_method_holder); + JNIHandles::destroy_weak_global(_hot_method_holder); + _method_holder = JNIHandles::make_global(method_holder); + if (_hot_method != NULL) { + _hot_method_holder = JNIHandles::make_global(Handle(thread, _hot_method->method_holder()->klass_holder())); + } + return this; +} + // ------------------------------------------------------------------ // CompileTask::code/set_code nmethod* CompileTask::code() const { @@ -363,12 +388,20 @@ void CompileTask::mark_on_stack() { // Mark these methods as something redefine classes cannot remove. + if (is_unloaded()) { + return; + } _method->set_on_stack(true); if (_hot_method != NULL) { _hot_method->set_on_stack(true); } } +bool CompileTask::is_unloaded() const { + return _method_holder != NULL && JNIHandles::is_weak_global_handle(_method_holder) && JNIHandles::is_global_weak_cleared(_method_holder); +} + + // ------------------------------------------------------------------ // CompileTask::print void CompileTask::print() { @@ -411,9 +444,19 @@ // CompileTask::print_compilation_impl void CompileTask::print_compilation_impl(outputStream* st, Method* method, int compile_id, int comp_level, bool is_osr_method, int osr_bci, bool is_blocking, - const char* msg, bool short_form) { + const char* msg, bool short_form, + jlong time_queued, jlong time_started) { if (!short_form) { - st->print("%7d ", (int) st->time_stamp().milliseconds()); // print timestamp + // Print current time + st->print("%7d ", (int)st->time_stamp().milliseconds()); + if (Verbose && time_queued != 0) { + // Print time in queue and time being processed by compiler thread + jlong now = os::elapsed_counter(); + st->print("%d ", (int)TimeHelper::counter_to_millis(now-time_queued)); + if (time_started != 0) { + st->print("%d ", (int)TimeHelper::counter_to_millis(now-time_started)); + } + } } st->print("%4d ", compile_id); // print compilation number @@ -528,7 +571,7 @@ // CompileTask::print_compilation void CompileTask::print_compilation(outputStream* st, const char* msg, bool short_form) { bool is_osr_method = osr_bci() != InvocationEntryBci; - print_compilation_impl(st, method(), compile_id(), comp_level(), is_osr_method, osr_bci(), is_blocking(), msg, short_form); + print_compilation_impl(st, is_unloaded() ? NULL : method(), compile_id(), comp_level(), is_osr_method, osr_bci(), is_blocking(), msg, short_form, _time_queued, _time_started); } // ------------------------------------------------------------------ @@ -750,6 +793,9 @@ { No_Safepoint_Verifier nsv; task = CompilationPolicy::policy()->select_task(this); + if (task != NULL) { + task = task->select_for_compilation(); + } } if (task != NULL) { remove(task); @@ -782,7 +828,7 @@ } void CompileQueue::remove(CompileTask* task) { - assert(lock()->owned_by_self(), "must own lock"); + assert(lock()->owned_by_self(), "must own lock"); if (task->prev() != NULL) { task->prev()->set_next(task->next()); } else { diff -r 18fd6d87f16f src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/compiler/compileBroker.hpp Tue Aug 27 14:12:19 2019 +0200 @@ -59,7 +59,8 @@ CompileTask* _next, *_prev; bool _is_free; // Fields used for logging why the compilation was initiated: - jlong _time_queued; // in units of os::elapsed_counter() + jlong _time_queued; // time when task was enqueued + jlong _time_started; // time when compilation started Method* _hot_method; // which method actually triggered this task jobject _hot_method_holder; int _hot_count; // information about its invocation counter @@ -94,10 +95,13 @@ void mark_complete() { _is_complete = true; } void mark_success() { _is_success = true; } + void mark_started(jlong time) { _time_started = time; } int comp_level() { return _comp_level;} void set_comp_level(int comp_level) { _comp_level = comp_level;} + CompileTask* select_for_compilation(); + int num_inlined_bytecodes() const { return _num_inlined_bytecodes; } void set_num_inlined_bytecodes(int n) { _num_inlined_bytecodes = n; } @@ -107,11 +111,14 @@ void set_prev(CompileTask* prev) { _prev = prev; } bool is_free() const { return _is_free; } void set_is_free(bool val) { _is_free = val; } + bool is_unloaded() const; private: static void print_compilation_impl(outputStream* st, Method* method, int compile_id, int comp_level, bool is_osr_method = false, int osr_bci = -1, bool is_blocking = false, - const char* msg = NULL, bool short_form = false); + const char* msg = NULL, bool short_form = false, + jlong time_queued = 0, jlong time_started = 0); + public: void print_compilation(outputStream* st = tty, const char* msg = NULL, bool short_form = false); diff -r 18fd6d87f16f src/share/vm/runtime/advancedThresholdPolicy.cpp --- a/src/share/vm/runtime/advancedThresholdPolicy.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/advancedThresholdPolicy.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -110,7 +110,7 @@ } } -// Check if this method has been stale from a given number of milliseconds. +// Check if this method has been stale for a given number of milliseconds. // See select_task(). bool AdvancedThresholdPolicy::is_stale(jlong t, jlong timeout, Method* m) { jlong delta_s = t - SafepointSynchronize::end_of_last_safepoint(); @@ -169,27 +169,24 @@ for (CompileTask* task = compile_queue->first(); task != NULL;) { CompileTask* next_task = task->next(); Method* method = task->method(); + // If a method was unloaded or has been stale for some time, remove it from the queue. + // Blocking tasks and tasks submitted from whitebox API don't become stale + if (task->is_unloaded() || is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) { + if (!task->is_unloaded()) { + if (PrintTieredEvents) { + print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel) task->comp_level()); + } + method->clear_queued_for_compilation(); + } + compile_queue->remove_and_mark_stale(task); + task = next_task; + continue; + } update_rate(t, method); - if (max_task == NULL) { + if (max_task == NULL || compare_methods(method, max_method)) { + // Select a method with the highest rate max_task = task; max_method = method; - } else { - // If a method has been stale for some time, remove it from the queue. - if (is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) { - if (PrintTieredEvents) { - print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel)task->comp_level()); - } - compile_queue->remove_and_mark_stale(task); - method->clear_queued_for_compilation(); - task = next_task; - continue; - } - - // Select a method with a higher rate - if (compare_methods(method, max_method)) { - max_task = task; - max_method = method; - } } task = next_task; } diff -r 18fd6d87f16f src/share/vm/runtime/compilationPolicy.cpp --- a/src/share/vm/runtime/compilationPolicy.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/compilationPolicy.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -340,6 +340,14 @@ } CompileTask* NonTieredCompPolicy::select_task(CompileQueue* compile_queue) { + // Remove unloaded methods from the queue + for (CompileTask* task = compile_queue->first(); task != NULL; ) { + CompileTask* next = task->next(); + if (task->is_unloaded()) { + compile_queue->remove_and_mark_stale(task); + } + task = next; + } return compile_queue->first(); } diff -r 18fd6d87f16f src/share/vm/runtime/jniHandles.cpp --- a/src/share/vm/runtime/jniHandles.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/jniHandles.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -126,6 +126,12 @@ template oop JNIHandles::resolve_jweak(jweak); template oop JNIHandles::resolve_jweak(jweak); +bool JNIHandles::is_global_weak_cleared(jweak handle) { + assert(is_jweak(handle), "not a weak handle"); + return guard_value(jweak_ref(handle)) == NULL; +} + + void JNIHandles::destroy_global(jobject handle) { if (handle != NULL) { assert(is_global_handle(handle), "Invalid delete of global JNI handle"); diff -r 18fd6d87f16f src/share/vm/runtime/jniHandles.hpp --- a/src/share/vm/runtime/jniHandles.hpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/jniHandles.hpp Tue Aug 27 14:12:19 2019 +0200 @@ -74,6 +74,7 @@ static jobject make_local(JNIEnv* env, oop obj); // Fast version when env is known static jobject make_local(Thread* thread, oop obj); // Even faster version when current thread is known inline static void destroy_local(jobject handle); + static bool is_global_weak_cleared(jweak handle); // Test jweak without resolution // Global handles static jobject make_global(Handle obj); diff -r 18fd6d87f16f src/share/vm/runtime/timer.cpp --- a/src/share/vm/runtime/timer.cpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/timer.cpp Tue Aug 27 14:12:19 2019 +0200 @@ -48,6 +48,10 @@ return counter/freq; } +double TimeHelper::counter_to_millis(jlong counter) { + return counter_to_seconds(counter) * 1000.0; +} + void elapsedTimer::add(elapsedTimer t) { _counter += t._counter; } diff -r 18fd6d87f16f src/share/vm/runtime/timer.hpp --- a/src/share/vm/runtime/timer.hpp Fri Sep 28 14:24:22 2018 +0200 +++ b/src/share/vm/runtime/timer.hpp Tue Aug 27 14:12:19 2019 +0200 @@ -123,6 +123,7 @@ class TimeHelper { public: static double counter_to_seconds(jlong counter); + static double counter_to_millis(jlong counter); }; #endif // SHARE_VM_RUNTIME_TIMER_HPP diff -r 18fd6d87f16f test/TEST.groups --- a/test/TEST.groups Fri Sep 28 14:24:22 2018 +0200 +++ b/test/TEST.groups Tue Aug 27 14:12:19 2019 +0200 @@ -1,4 +1,4 @@ -# +test/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java# # Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -128,6 +128,9 @@ hotspot_wbapitest = \ sanity/ +hotspot_mytest = \ + compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java + hotspot_compiler = \ sanity/ExecuteInternalVMTests.java diff -r 18fd6d87f16f test/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java Tue Aug 27 14:12:19 2019 +0200 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * 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. + */ + +/* + * @test TestOverloadCompileQueues + * @bug 8163511 + * @summary Test overloading the C1 and C2 compile queues with tasks. + * @run main/othervm -XX:-TieredCompilation -XX:CompileThreshold=2 -XX:CICompilerCount=1 + * compiler.classUnloading.methodUnloading.TestOverloadCompileQueues + * @run main/othervm -XX:TieredCompileTaskTimeout=1000 -XX:CICompilerCount=2 + * -XX:Tier0InvokeNotifyFreqLog=0 -XX:Tier2InvokeNotifyFreqLog=1 -XX:Tier3InvokeNotifyFreqLog=0 + * -XX:Tier23InlineeNotifyFreqLog=10 -XX:Tier0BackedgeNotifyFreqLog=0 -XX:Tier2BackedgeNotifyFreqLog=4 + * -XX:Tier3BackedgeNotifyFreqLog=3 -XX:Tier2CompileThreshold=0 -XX:Tier2BackEdgeThreshold=0 + * -XX:Tier3InvocationThreshold=0 -XX:Tier3MinInvocationThreshold=0 -XX:Tier3CompileThreshold=2 + * -XX:Tier3BackEdgeThreshold=60 -XX:Tier4InvocationThreshold=5 -XX:Tier4MinInvocationThreshold=0 + * -XX:Tier4CompileThreshold=15 -XX:Tier4BackEdgeThreshold=40 + * compiler.classUnloading.methodUnloading.TestOverloadCompileQueues + */ + +package compiler.classUnloading.methodUnloading; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +public class TestOverloadCompileQueues { + public static final int ITERS = 1000; // Increase for longer stress testing + + // Some methods to fill up the compile queue + public static int test0() { return 0; } + public static int test1() { return 0; } + public static int test2() { return 0; } + public static int test3() { return 0; } + public static int test4() { return 0; } + public static int test5() { return 0; } + public static int test6() { return 0; } + public static int test7() { return 0; } + public static int test8() { return 0; } + public static int test9() { return 0; } + public static int test10() { return 0; } + public static int test11() { return 0; } + public static int test12() { return 0; } + public static int test13() { return 0; } + public static int test14() { return 0; } + public static int test15() { return 0; } + public static int test16() { return 0; } + public static int test17() { return 0; } + public static int test18() { return 0; } + public static int test19() { return 0; } + + public static void main(String[] args) throws Throwable { + Class thisClass = TestOverloadCompileQueues.class; + ClassLoader defaultLoader = thisClass.getClassLoader(); + URL classesDir = thisClass.getProtectionDomain().getCodeSource().getLocation(); + + for (int i = 0; i < ITERS; ++i) { + // Load test class with own class loader + URLClassLoader myLoader = URLClassLoader.newInstance(new URL[] {classesDir}, defaultLoader.getParent()); + Class testClass = Class.forName(thisClass.getCanonicalName(), true, myLoader); + + // Execute all test methods to trigger compilation and fill up compile queue + for (int j = 0; j < 20; ++j) { + Method method = testClass.getDeclaredMethod("test" + j); + method.invoke(null); + method.invoke(null); + } + + // Unload dead classes from ealier iterations + System.gc(); + } + } +}