--- old/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp 2019-07-03 19:47:39.000000000 -0700 +++ new/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp 2019-07-03 19:47:39.000000000 -0700 @@ -393,6 +393,10 @@ Service_lock->unlock(); } + if (Notification_lock->owned_by_self()) { + Notification_lock->unlock(); + } + if (CodeCache_lock->owned_by_self()) { CodeCache_lock->unlock(); } --- old/src/hotspot/share/runtime/mutexLocker.cpp 2019-07-03 19:47:40.000000000 -0700 +++ new/src/hotspot/share/runtime/mutexLocker.cpp 2019-07-03 19:47:40.000000000 -0700 @@ -128,6 +128,7 @@ Mutex* Management_lock = NULL; Monitor* Service_lock = NULL; +Monitor* Notification_lock = NULL; Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; @@ -257,6 +258,7 @@ def(Patching_lock , PaddedMutex , special, true, Monitor::_safepoint_check_never); // used for safepointing and code patching. def(Service_lock , PaddedMonitor, special, true, Monitor::_safepoint_check_never); // used for service thread operations + def(Notification_lock , PaddedMonitor, special, true, Monitor::_safepoint_check_never); // used for notification thread operations def(JmethodIdCreation_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_always); // used for creating jmethodIDs. def(SystemDictionary_lock , PaddedMonitor, leaf, true, Monitor::_safepoint_check_always); --- old/src/hotspot/share/runtime/mutexLocker.hpp 2019-07-03 19:47:41.000000000 -0700 +++ new/src/hotspot/share/runtime/mutexLocker.hpp 2019-07-03 19:47:41.000000000 -0700 @@ -122,6 +122,7 @@ extern Mutex* Management_lock; // a lock used to serialize JVM management extern Monitor* Service_lock; // a lock used for service thread operation +extern Monitor* Notification_lock; // a lock used for notification thread operation extern Monitor* PeriodicTask_lock; // protects the periodic task structure extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition extern Monitor* ThreadsSMRDelete_lock; // Used by ThreadsSMRSupport to take pressure off the Threads_lock --- old/src/hotspot/share/runtime/serviceThread.cpp 2019-07-03 19:47:42.000000000 -0700 +++ new/src/hotspot/share/runtime/serviceThread.cpp 2019-07-03 19:47:42.000000000 -0700 @@ -37,10 +37,6 @@ #include "runtime/os.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/resolvedMethodTable.hpp" -#include "services/diagnosticArgument.hpp" -#include "services/diagnosticFramework.hpp" -#include "services/gcNotifier.hpp" -#include "services/lowMemoryDetector.hpp" ServiceThread* ServiceThread::_instance = NULL; @@ -117,10 +113,7 @@ const size_t oopstorage_count = ARRAY_SIZE(oopstorages); while (true) { - bool sensors_changed = false; bool has_jvmti_events = false; - bool has_gc_notification_event = false; - bool has_dcmd_notification_event = false; bool stringtable_work = false; bool symboltable_work = false; bool resolved_method_table_work = false; @@ -144,10 +137,7 @@ // only the first recognized bit of work, to avoid frequently true early // tests from potentially starving later work. Hence the use of // arithmetic-or to combine results; we don't want short-circuiting. - while (((sensors_changed = LowMemoryDetector::has_pending_requests()) | - (has_jvmti_events = JvmtiDeferredEventQueue::has_events()) | - (has_gc_notification_event = GCNotifier::has_event()) | - (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) | + while (((has_jvmti_events = JvmtiDeferredEventQueue::has_events()) | (stringtable_work = StringTable::has_work()) | (symboltable_work = SymbolTable::has_work()) | (resolved_method_table_work = ResolvedMethodTable::has_work()) | @@ -178,18 +168,6 @@ jvmti_event.post(); } - if (sensors_changed) { - LowMemoryDetector::process_sensor_changes(jt); - } - - if(has_gc_notification_event) { - GCNotifier::sendNotification(CHECK); - } - - if(has_dcmd_notification_event) { - DCmdFactory::send_notification(CHECK); - } - if (resolved_method_table_work) { ResolvedMethodTable::do_concurrent_work(jt); } --- old/src/hotspot/share/runtime/serviceThread.hpp 2019-07-03 19:47:43.000000000 -0700 +++ new/src/hotspot/share/runtime/serviceThread.hpp 2019-07-03 19:47:43.000000000 -0700 @@ -27,8 +27,10 @@ #include "runtime/thread.hpp" -// A JavaThread for low memory detection support and JVMTI -// compiled-method-load events. +// A hidden from external view JavaThread for JVMTI compiled-method-load +// events, oop storage cleanup, and the maintainance of string, symbol, +// protection domain, and resolved method tables. + class ServiceThread : public JavaThread { friend class VMStructs; private: --- old/src/hotspot/share/runtime/vmStructs.cpp 2019-07-03 19:47:44.000000000 -0700 +++ new/src/hotspot/share/runtime/vmStructs.cpp 2019-07-03 19:47:44.000000000 -0700 @@ -88,6 +88,7 @@ #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/perfMemory.hpp" #include "runtime/serviceThread.hpp" @@ -1369,6 +1370,7 @@ declare_type(JavaThread, Thread) \ declare_type(JvmtiAgentThread, JavaThread) \ declare_type(ServiceThread, JavaThread) \ + declare_type(NotificationThread, JavaThread) \ declare_type(CompilerThread, JavaThread) \ declare_type(CodeCacheSweeperThread, JavaThread) \ declare_toplevel_type(OSThread) \ --- old/src/hotspot/share/services/diagnosticFramework.cpp 2019-07-03 19:47:45.000000000 -0700 +++ new/src/hotspot/share/services/diagnosticFramework.cpp 2019-07-03 19:47:45.000000000 -0700 @@ -437,9 +437,9 @@ } void DCmdFactory::push_jmx_notification_request() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); _has_pending_jmx_notification = true; - Service_lock->notify_all(); + Notification_lock->notify_all(); } void DCmdFactory::send_notification(TRAPS) { @@ -455,7 +455,7 @@ HandleMark hm(THREAD); bool notif = false; { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); notif = _has_pending_jmx_notification; _has_pending_jmx_notification = false; } --- old/src/hotspot/share/services/gcNotifier.cpp 2019-07-03 19:47:46.000000000 -0700 +++ new/src/hotspot/share/services/gcNotifier.cpp 2019-07-03 19:47:46.000000000 -0700 @@ -54,18 +54,18 @@ } void GCNotifier::addRequest(GCNotificationRequest *request) { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); if(first_request == NULL) { first_request = request; } else { last_request->next = request; } last_request = request; - Service_lock->notify_all(); + Notification_lock->notify_all(); } GCNotificationRequest *GCNotifier::getRequest() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); GCNotificationRequest *request = first_request; if(first_request != NULL) { first_request = first_request->next; --- old/src/hotspot/share/services/lowMemoryDetector.cpp 2019-07-03 19:47:47.000000000 -0700 +++ new/src/hotspot/share/services/lowMemoryDetector.cpp 2019-07-03 19:47:47.000000000 -0700 @@ -40,7 +40,7 @@ volatile jint LowMemoryDetector::_disabled_count = 0; bool LowMemoryDetector::has_pending_requests() { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); bool has_requests = false; int num_memory_pools = MemoryService::num_memory_pools(); for (int i = 0; i < num_memory_pools; i++) { @@ -62,7 +62,7 @@ ResourceMark rm(THREAD); HandleMark hm(THREAD); - // No need to hold Service_lock to call out to Java + // No need to hold Notification_lock to call out to Java int num_memory_pools = MemoryService::num_memory_pools(); for (int i = 0; i < num_memory_pools; i++) { MemoryPool* pool = MemoryService::get_memory_pool(i); @@ -80,7 +80,7 @@ // This method could be called from any Java threads // and also VMThread. void LowMemoryDetector::detect_low_memory() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); bool has_pending_requests = false; int num_memory_pools = MemoryService::num_memory_pools(); @@ -98,7 +98,7 @@ } if (has_pending_requests) { - Service_lock->notify_all(); + Notification_lock->notify_all(); } } @@ -113,14 +113,14 @@ } { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); MemoryUsage usage = pool->get_memory_usage(); sensor->set_gauge_sensor_level(usage, pool->usage_threshold()); if (sensor->has_pending_requests()) { // notify sensor state update - Service_lock->notify_all(); + Notification_lock->notify_all(); } } } @@ -135,14 +135,14 @@ } { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); MemoryUsage usage = pool->get_last_collection_usage(); sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold()); if (sensor->has_pending_requests()) { // notify sensor state update - Service_lock->notify_all(); + Notification_lock->notify_all(); } } } @@ -205,7 +205,7 @@ // If the current level is between high and low threshold, no change. // void SensorInfo::set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold) { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); assert(high_low_threshold->is_high_threshold_supported(), "just checking"); bool is_over_high = high_low_threshold->is_high_threshold_crossed(usage); @@ -260,7 +260,7 @@ // the sensor will be on (i.e. sensor is currently off // and has pending trigger requests). void SensorInfo::set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold) { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); assert(counter_threshold->is_high_threshold_supported(), "just checking"); bool is_over_high = counter_threshold->is_high_threshold_crossed(usage); @@ -334,8 +334,8 @@ } { - // Holds Service_lock and update the sensor state - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + // Holds Notification_lock and update the sensor state + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); assert(_pending_trigger_count > 0, "Must have pending trigger"); _sensor_on = true; _sensor_count += count; @@ -345,8 +345,8 @@ void SensorInfo::clear(int count, TRAPS) { { - // Holds Service_lock and update the sensor state - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + // Holds Notification_lock and update the sensor state + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); if (_pending_clear_count == 0) { // Bail out if we lost a race to set_*_sensor_level() which may have // reactivated the sensor in the meantime because it was triggered again. --- old/src/hotspot/share/services/lowMemoryDetector.hpp 2019-07-03 19:47:48.000000000 -0700 +++ new/src/hotspot/share/services/lowMemoryDetector.hpp 2019-07-03 19:47:48.000000000 -0700 @@ -59,7 +59,7 @@ // // May need to deal with hysteresis effect. // -// Memory detection code runs in the Service thread (serviceThread.hpp). +// Memory detection code runs in the Notification thread (notificationThread.hpp). class OopClosure; class MemoryPool; @@ -213,7 +213,7 @@ class LowMemoryDetector : public AllStatic { friend class LowMemoryDetectorDisabler; - friend class ServiceThread; + friend class NotificationThread; private: // true if any collected heap has low memory detection enabled static volatile bool _enabled_for_collected_pools; --- old/src/hotspot/share/services/management.cpp 2019-07-03 19:47:49.000000000 -0700 +++ new/src/hotspot/share/services/management.cpp 2019-07-03 19:47:49.000000000 -0700 @@ -44,6 +44,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/serviceThread.hpp" #include "runtime/thread.inline.hpp" @@ -148,6 +149,7 @@ void Management::initialize(TRAPS) { // Start the service thread ServiceThread::initialize(); + NotificationThread::initialize(); if (ManagementServer) { ResourceMark rm(THREAD); --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java 2019-07-03 19:47:50.000000000 -0700 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java 2019-07-03 19:47:50.000000000 -0700 @@ -158,6 +158,7 @@ } virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); virtualConstructor.addMapping("ServiceThread", ServiceThread.class); + virtualConstructor.addMapping("NotificationThread", NotificationThread.class); } public Threads() { @@ -165,7 +166,7 @@ } /** NOTE: this returns objects of type JavaThread, CompilerThread, - JvmtiAgentThread, and ServiceThread. + JvmtiAgentThread, NotificationThread, and ServiceThread. The latter four are subclasses of the former. Most operations (fetching the top frame, etc.) are only allowed to be performed on a "pure" JavaThread. For this reason, {@link --- /dev/null 2019-07-03 19:47:51.000000000 -0700 +++ new/src/hotspot/share/runtime/notificationThread.cpp 2019-07-03 19:47:51.000000000 -0700 @@ -0,0 +1,128 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "memory/universe.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/notificationThread.hpp" +#include "services/lowMemoryDetector.hpp" +#include "services/gcNotifier.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" + +NotificationThread* NotificationThread::_instance = NULL; + +void NotificationThread::initialize() { + EXCEPTION_MARK; + + const char* name = "Notification Thread"; + Handle string = java_lang_String::create_from_str(name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + Handle thread_oop = JavaCalls::construct_new_instance( + SystemDictionary::Thread_klass(), + vmSymbols::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + Klass* group = SystemDictionary::ThreadGroup_klass(); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, + thread_group, + group, + vmSymbols::add_method_name(), + vmSymbols::thread_void_signature(), + thread_oop, + THREAD); + { + MutexLocker mu(Threads_lock); + NotificationThread* thread = new NotificationThread(¬ification_thread_entry); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (thread == NULL || thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + os::native_thread_creation_failed_msg()); + } + + java_lang_Thread::set_thread(thread_oop(), thread); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + thread->set_threadObj(thread_oop()); + _instance = thread; + + Threads::add(thread); + Thread::start(thread); + } +} + + + +void NotificationThread::notification_thread_entry(JavaThread* jt, TRAPS) { + while (true) { + bool sensors_changed = false; + bool has_dcmd_notification_event = false; + bool has_gc_notification_event = false; + { + // Need state transition ThreadBlockInVM so that this thread + // will be handled by safepoint correctly when this thread is + // notified at a safepoint. + + ThreadBlockInVM tbivm(jt); + + MonitorLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); + // Process all available work on each (outer) iteration, rather than + // only the first recognized bit of work, to avoid frequently true early + // tests from potentially starving later work. Hence the use of + // arithmetic-or to combine results; we don't want short-circuiting. + while (((sensors_changed = LowMemoryDetector::has_pending_requests()) | + (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) | + (has_gc_notification_event = GCNotifier::has_event())) + == 0) { + // Wait until notified that there is some work to do. + ml.wait(); + } + + } + + if (sensors_changed) { + LowMemoryDetector::process_sensor_changes(jt); + } + + if(has_gc_notification_event) { + GCNotifier::sendNotification(CHECK); + } + + if(has_dcmd_notification_event) { + DCmdFactory::send_notification(CHECK); + } + + } +} + --- /dev/null 2019-07-03 19:47:52.000000000 -0700 +++ new/src/hotspot/share/runtime/notificationThread.hpp 2019-07-03 19:47:52.000000000 -0700 @@ -0,0 +1,49 @@ +/* + * 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. + * + */ + +#ifndef SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP +#define SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP + +#include "runtime/thread.hpp" + +// A JavaThread for low memory detection support, GC and +// diagnostic framework notifications. This thread is not hidden +// from the external view to allow the debugger to stop at the +// breakpoints inside registred MXBean notification listeners. + +class NotificationThread : public JavaThread { + friend class VMStructs; + private: + + static NotificationThread* _instance; + + static void notification_thread_entry(JavaThread* thread, TRAPS); + NotificationThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; + + public: + static void initialize(); + +}; + +#endif // SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP --- /dev/null 2019-07-03 19:47:53.000000000 -0700 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java 2019-07-03 19:47:53.000000000 -0700 @@ -0,0 +1,37 @@ +/* + * 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. + * + */ + +package sun.jvm.hotspot.runtime; + + +import sun.jvm.hotspot.debugger.Address; + +public class NotificationThread extends JavaThread { + public NotificationThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + +}