8170299: Debugger does not stop inside the low memory notifications code

Reviewed-by: sspitsyn, dholmes
This commit is contained in:
Daniil Titov 2019-10-08 09:13:08 -07:00
parent dcceed10b6
commit 7306526835
17 changed files with 427 additions and 37 deletions

View File

@ -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();
}

View File

@ -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") \
\

View File

@ -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);

View File

@ -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

View 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(&notification_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);
}
}
}

View 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

View File

@ -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) {

View File

@ -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:

View File

@ -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) \

View File

@ -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;
}

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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; }
}

View File

@ -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) {

View 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;
}
}