8170299: Debugger does not stop inside the low memory notifications code
Reviewed-by: sspitsyn, dholmes
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Tue Oct 08 09:13:08 2019 -0700
@@ -393,6 +393,10 @@
Service_lock->unlock();
}
+ if (UseNotificationThread && Notification_lock->owned_by_self()) {
+ Notification_lock->unlock();
+ }
+
if (CodeCache_lock->owned_by_self()) {
CodeCache_lock->unlock();
}
--- a/src/hotspot/share/runtime/globals.hpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/globals.hpp Tue Oct 08 09:13:08 2019 -0700
@@ -1047,6 +1047,9 @@
diagnostic(bool, EnableThreadSMRStatistics, trueInDebug, \
"Enable Thread SMR Statistics") \
\
+ product(bool, UseNotificationThread, true, \
+ "Use Notification Thread") \
+ \
product(bool, Inline, true, \
"Enable inlining") \
\
--- a/src/hotspot/share/runtime/mutexLocker.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/mutexLocker.cpp Tue Oct 08 09:13:08 2019 -0700
@@ -115,6 +115,7 @@
Mutex* Management_lock = NULL;
Monitor* Service_lock = NULL;
+Monitor* Notification_lock = NULL;
Monitor* PeriodicTask_lock = NULL;
Monitor* RedefineClasses_lock = NULL;
Mutex* Verify_lock = NULL;
@@ -236,6 +237,13 @@
def(Patching_lock , PaddedMutex , special, true, _safepoint_check_never); // used for safepointing and code patching.
def(CompiledMethod_lock , PaddedMutex , special-1, true, _safepoint_check_never);
def(Service_lock , PaddedMonitor, special, true, _safepoint_check_never); // used for service thread operations
+
+ if (UseNotificationThread) {
+ def(Notification_lock , PaddedMonitor, special, true, _safepoint_check_never); // used for notification thread operations
+ } else {
+ Notification_lock = Service_lock;
+ }
+
def(JmethodIdCreation_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for creating jmethodIDs.
def(SystemDictionary_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
--- a/src/hotspot/share/runtime/mutexLocker.hpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/mutexLocker.hpp Tue Oct 08 09:13:08 2019 -0700
@@ -111,6 +111,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 Mutex* Verify_lock; // synchronize initialization of verify library
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/notificationThread.cpp Tue Oct 08 09:13:08 2019 -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/diagnosticArgument.hpp"
+#include "services/diagnosticFramework.hpp"
+#include "services/gcNotifier.hpp"
+#include "services/lowMemoryDetector.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 as a suspend equalent until notified that there is some work to do.
+ ml.wait(0, true);
+ }
+
+ }
+
+ 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 Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/notificationThread.hpp Tue Oct 08 09:13:08 2019 -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
--- a/src/hotspot/share/runtime/serviceThread.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/serviceThread.cpp Tue Oct 08 09:13:08 2019 -0700
@@ -120,10 +120,10 @@
// 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()) |
+ while (((sensors_changed = (!UseNotificationThread && 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()) |
+ (has_gc_notification_event = (!UseNotificationThread && GCNotifier::has_event())) |
+ (has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) |
(stringtable_work = StringTable::has_work()) |
(symboltable_work = SymbolTable::has_work()) |
(resolved_method_table_work = ResolvedMethodTable::has_work()) |
@@ -151,16 +151,18 @@
jvmti_event.post();
}
- if (sensors_changed) {
- LowMemoryDetector::process_sensor_changes(jt);
- }
+ if (!UseNotificationThread) {
+ if (sensors_changed) {
+ LowMemoryDetector::process_sensor_changes(jt);
+ }
- if(has_gc_notification_event) {
- GCNotifier::sendNotification(CHECK);
- }
+ if(has_gc_notification_event) {
+ GCNotifier::sendNotification(CHECK);
+ }
- if(has_dcmd_notification_event) {
- DCmdFactory::send_notification(CHECK);
+ if(has_dcmd_notification_event) {
+ DCmdFactory::send_notification(CHECK);
+ }
}
if (resolved_method_table_work) {
--- a/src/hotspot/share/runtime/serviceThread.hpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/serviceThread.hpp Tue Oct 08 09:13:08 2019 -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:
--- a/src/hotspot/share/runtime/vmStructs.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/vmStructs.cpp Tue Oct 08 09:13:08 2019 -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"
@@ -1366,6 +1367,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) \
--- a/src/hotspot/share/services/diagnosticFramework.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/diagnosticFramework.cpp Tue Oct 08 09:13:08 2019 -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;
}
--- a/src/hotspot/share/services/gcNotifier.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/gcNotifier.cpp Tue Oct 08 09:13:08 2019 -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;
--- a/src/hotspot/share/services/lowMemoryDetector.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/lowMemoryDetector.cpp Tue Oct 08 09:13:08 2019 -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.
--- a/src/hotspot/share/services/lowMemoryDetector.hpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/lowMemoryDetector.hpp Tue Oct 08 09:13:08 2019 -0700
@@ -59,7 +59,8 @@
//
// 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 or
+// ServiceThread depending on UseNotificationThread flag.
class OopClosure;
class MemoryPool;
@@ -214,6 +215,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;
--- a/src/hotspot/share/services/management.cpp Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/management.cpp Tue Oct 08 09:13:08 2019 -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,7 +149,9 @@
void Management::initialize(TRAPS) {
// Start the service thread
ServiceThread::initialize();
-
+ if (UseNotificationThread) {
+ NotificationThread::initialize();
+ }
if (ManagementServer) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java Tue Oct 08 09:13:08 2019 -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; }
+
+}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java Tue Oct 08 15:03:20 2019 +0100
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java Tue Oct 08 09:13:08 2019 -0700
@@ -158,6 +158,7 @@
}
virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
+ virtualConstructor.addMapping("NotificationThread", NotificationThread.class);
}
public Threads() {
@@ -165,14 +166,14 @@
}
/** 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
sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been
changed from the definition in the VM (which returns true for
all of these thread types) to return true for JavaThreads and
- false for the three subclasses. FIXME: should reconsider the
+ false for the four subclasses. FIXME: should reconsider the
inheritance hierarchy; see {@link
sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */
public JavaThread getJavaThreadAt(int i) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java Tue Oct 08 09:13:08 2019 -0700
@@ -0,0 +1,148 @@
+/*
+ * 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
+ * @summary Tests that the breakpoint in the notification listener is hit when the
+ * notification thread is enabled and is not hit when the notification thread is disabled
+ * (the service thread delivers the notifications in this case).
+ *
+ * @library /test/lib
+ * @run compile -g JdbStopInNotificationThreadTest.java
+ * @run main/othervm JdbStopInNotificationThreadTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import lib.jdb.JdbCommand;
+import lib.jdb.JdbTest;
+
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import java.lang.management.*;
+import java.util.Collection;
+import java.util.LinkedList;
+
+class JdbStopInNotificationThreadTestTarg {
+
+ private static volatile boolean done = false;
+
+ private static final MemoryPoolMXBean tenuredGenPool =
+ findTenuredGenPool();
+
+ public static void main(String[] args) throws Exception {
+ test(); // @1 breakpoint
+ }
+
+ private static void test() throws Exception {
+ setPercentageUsageThreshold(0.1);
+ MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
+ NotificationEmitter emitter = (NotificationEmitter) mbean;
+ emitter.addNotificationListener(new NotificationListener() {
+ public void handleNotification(Notification n, Object hb) {
+ System.out.println("Notification received:" + n.getType());
+ if (n.getType().equals(
+ MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
+ done = true;
+ System.out.println("Notification MEMORY_THRESHOLD_EXCEEDED received:");
+ long maxMemory = tenuredGenPool.getUsage().getMax();
+ long usedMemory = tenuredGenPool.getUsage().getUsed();
+ System.out.println("Memory usage low!!!"); // @2 breakpoint
+ double percentageUsed = ((double) usedMemory) / maxMemory;
+ System.out.println("percentageUsed = " + percentageUsed);
+ }
+ }
+ }, null, null);
+
+ Collection<Object[]> numbers = new LinkedList();
+ long counter = 0;
+ while (!done) {
+ numbers.add(new Object[1000]);
+ counter++;
+ if (counter % 1000 == 0) {
+ Thread.sleep(100);
+ }
+ }
+ System.out.println("Done");
+ }
+
+ private static MemoryPoolMXBean findTenuredGenPool() {
+ for (MemoryPoolMXBean pool :
+ ManagementFactory.getMemoryPoolMXBeans()) {
+ if (pool.getType() == MemoryType.HEAP &&
+ pool.isUsageThresholdSupported()) {
+ return pool;
+ }
+ }
+ throw new RuntimeException("Could not find tenured space");
+ }
+
+ public static void setPercentageUsageThreshold(double percentage) {
+ if (percentage <= 0.0 || percentage > 1.0) {
+ throw new IllegalArgumentException("Percentage not in range");
+ }
+ System.out.println("Setting threashold for pool " + tenuredGenPool.getName() + " percentage:" + percentage);
+ long maxMemory = tenuredGenPool.getUsage().getMax();
+ long warningThreshold = (long) (maxMemory * percentage);
+ tenuredGenPool.setUsageThreshold(warningThreshold);
+ }
+}
+
+public class JdbStopInNotificationThreadTest extends JdbTest {
+
+ private static final String DEBUGGEE_CLASS = JdbStopInNotificationThreadTestTarg.class.getName();
+ private static final String PATTERN1_TEMPLATE = "^Breakpoint hit: \"thread=Notification Thread\", " +
+ "JdbStopInNotificationThreadTestTarg\\$1\\.handleNotification\\(\\), line=%LINE_NUMBER.*\\R%LINE_NUMBER\\s+System\\.out\\.println\\(\"Memory usage low!!!\"\\);.*";
+
+ private JdbStopInNotificationThreadTest() {
+ super(DEBUGGEE_CLASS);
+ }
+
+ public static void main(String argv[]) {
+ new JdbStopInNotificationThreadTest().run();
+ }
+
+ @Override
+ protected void runCases() {
+ if (isNotificationThreadDisabled()) {
+ System.out.println("Notification Thread is disabled. Skipping the test");
+ return;
+ }
+ int bpLine2 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 2).get(0);
+ jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS + "$1", bpLine2));
+ String pattern = PATTERN1_TEMPLATE.replaceAll("%LINE_NUMBER", String.valueOf(bpLine2));
+ jdb.command(JdbCommand.cont());
+ new OutputAnalyzer(jdb.getJdbOutput()).shouldMatch(pattern);
+ }
+
+ private boolean isNotificationThreadDisabled() {
+ int bpLine1 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 1).get(0);
+ jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS, bpLine1));
+ jdb.command(JdbCommand.run());
+ jdb.command(JdbCommand.threads());
+ if (new OutputAnalyzer(jdb.getJdbOutput()).getOutput().contains("Notification Thread")) {
+ return false;
+ }
+ return true;
+ }
+}