mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-08 20:49:44 +00:00
8170299: Debugger does not stop inside the low memory notifications code
Reviewed-by: sspitsyn, dholmes
This commit is contained in:
parent
dcceed10b6
commit
7306526835
@ -393,6 +393,10 @@ static void prepare_for_emergency_dump(Thread* thread) {
|
||||
Service_lock->unlock();
|
||||
}
|
||||
|
||||
if (UseNotificationThread && Notification_lock->owned_by_self()) {
|
||||
Notification_lock->unlock();
|
||||
}
|
||||
|
||||
if (CodeCache_lock->owned_by_self()) {
|
||||
CodeCache_lock->unlock();
|
||||
}
|
||||
|
||||
@ -1047,6 +1047,9 @@ const size_t minimumSymbolTableSize = 1024;
|
||||
diagnostic(bool, EnableThreadSMRStatistics, trueInDebug, \
|
||||
"Enable Thread SMR Statistics") \
|
||||
\
|
||||
product(bool, UseNotificationThread, true, \
|
||||
"Use Notification Thread") \
|
||||
\
|
||||
product(bool, Inline, true, \
|
||||
"Enable inlining") \
|
||||
\
|
||||
|
||||
@ -115,6 +115,7 @@ Monitor* RootRegionScan_lock = NULL;
|
||||
|
||||
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 @@ void mutex_init() {
|
||||
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);
|
||||
|
||||
@ -111,6 +111,7 @@ extern Monitor* RootRegionScan_lock; // used to notify that the CM t
|
||||
|
||||
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
|
||||
|
||||
128
src/hotspot/share/runtime/notificationThread.cpp
Normal file
128
src/hotspot/share/runtime/notificationThread.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
49
src/hotspot/share/runtime/notificationThread.hpp
Normal file
49
src/hotspot/share/runtime/notificationThread.hpp
Normal file
@ -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
|
||||
@ -120,10 +120,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
// 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 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
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) {
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
||||
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) \
|
||||
|
||||
@ -437,9 +437,9 @@ GrowableArray<DCmdArgumentInfo*>* DCmdWithParser::argument_info_array() const {
|
||||
}
|
||||
|
||||
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 @@ void DCmdFactory::send_notification_internal(TRAPS) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -54,18 +54,18 @@ void GCNotifier::pushNotification(GCMemoryManager *mgr, const char *action, cons
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -40,7 +40,7 @@ volatile bool LowMemoryDetector::_enabled_for_collected_pools = false;
|
||||
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 @@ void LowMemoryDetector::process_sensor_changes(TRAPS) {
|
||||
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 @@ void LowMemoryDetector::process_sensor_changes(TRAPS) {
|
||||
// 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 @@ void LowMemoryDetector::detect_low_memory() {
|
||||
}
|
||||
|
||||
if (has_pending_requests) {
|
||||
Service_lock->notify_all();
|
||||
Notification_lock->notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,14 +113,14 @@ void LowMemoryDetector::detect_low_memory(MemoryPool* pool) {
|
||||
}
|
||||
|
||||
{
|
||||
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 @@ void LowMemoryDetector::detect_after_gc_memory(MemoryPool* pool) {
|
||||
}
|
||||
|
||||
{
|
||||
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 @@ SensorInfo::SensorInfo() {
|
||||
// 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 @@ void SensorInfo::set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* hig
|
||||
// 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 @@ void SensorInfo::trigger(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);
|
||||
assert(_pending_trigger_count > 0, "Must have pending trigger");
|
||||
_sensor_on = true;
|
||||
_sensor_count += count;
|
||||
@ -345,8 +345,8 @@ void SensorInfo::trigger(int count, TRAPS) {
|
||||
|
||||
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.
|
||||
|
||||
@ -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 @@ public:
|
||||
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;
|
||||
|
||||
@ -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::init() {
|
||||
void Management::initialize(TRAPS) {
|
||||
// Start the service thread
|
||||
ServiceThread::initialize();
|
||||
|
||||
if (UseNotificationThread) {
|
||||
NotificationThread::initialize();
|
||||
}
|
||||
if (ManagementServer) {
|
||||
ResourceMark rm(THREAD);
|
||||
HandleMark hm(THREAD);
|
||||
|
||||
@ -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; }
|
||||
|
||||
}
|
||||
@ -158,6 +158,7 @@ public class Threads {
|
||||
}
|
||||
virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
|
||||
virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
|
||||
virtualConstructor.addMapping("NotificationThread", NotificationThread.class);
|
||||
}
|
||||
|
||||
public Threads() {
|
||||
@ -165,14 +166,14 @@ public class Threads {
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
|
||||
148
test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java
Normal file
148
test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user