diff --git a/src/hotspot/share/include/jmm.h b/src/hotspot/share/include/jmm.h index ee1c77e504a..c983dbfaf35 100644 --- a/src/hotspot/share/include/jmm.h +++ b/src/hotspot/share/include/jmm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -316,7 +316,8 @@ typedef struct jmmInterface_1_ { void (JNICALL *SetVMGlobal) (JNIEnv *env, jstring flag_name, jvalue new_value); - void* reserved6; + jlong (JNICALL *GetTotalThreadAllocatedMemory) + (JNIEnv *env); jobjectArray (JNICALL *DumpThreads) (JNIEnv *env, jlongArray ids, jboolean lockedMonitors, diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 90bf00a1b13..3051d71289d 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -74,7 +74,7 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list. extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list. -extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools. +extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark. extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops. extern Monitor* ConcurrentGCBreakpoints_lock; // Protects concurrent GC breakpoint management extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc) diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index a6c1efebac5..f5389493452 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -47,6 +47,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" @@ -413,8 +414,6 @@ static MemoryPool* get_memory_pool_from_jobject(jobject obj, TRAPS) { return MemoryService::get_memory_pool(ph); } -#endif // INCLUDE_MANAGEMENT - static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) { int num_threads = ids_ah->length(); @@ -430,8 +429,6 @@ static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) { } } -#if INCLUDE_MANAGEMENT - static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) { // check if the element of infoArray is of type ThreadInfo class Klass* threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK); @@ -2065,7 +2062,41 @@ jlong Management::ticks_to_ms(jlong ticks) { return (jlong)(((double)ticks / (double)os::elapsed_frequency()) * (double)1000.0); } -#endif // INCLUDE_MANAGEMENT + +// Gets the amount of memory allocated on the Java heap since JVM launch. +JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env)) + // A thread increments exited_allocated_bytes in ThreadService::remove_thread + // only after it removes itself from the threads list, and once a TLH is + // created, no thread it references can remove itself from the threads + // list, so none can update exited_allocated_bytes. We therefore initialize + // result with exited_allocated_bytes after after we create the TLH so that + // the final result can only be short due to (1) threads that start after + // the TLH is created, or (2) terminating threads that escape TLH creation + // and don't update exited_allocated_bytes before we initialize result. + + // We keep a high water mark to ensure monotonicity in case threads counted + // on a previous call end up in state (2). + static jlong high_water_result = 0; + + JavaThreadIteratorWithHandle jtiwh; + jlong result = ThreadService::exited_allocated_bytes(); + for (; JavaThread* thread = jtiwh.next();) { + jlong size = thread->cooked_allocated_bytes(); + result += size; + } + + { + MutexLocker ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag); + if (result < high_water_result) { + // Encountered (2) above, or result wrapped to a negative value. In + // the latter case, it's pegged at the last positive value. + result = high_water_result; + } else { + high_water_result = result; + } + } + return result; +JVM_END // Gets the amount of memory allocated on the Java heap for a single thread. // Returns -1 if the thread does not exist or has terminated. @@ -2199,9 +2230,6 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids, } JVM_END - - -#if INCLUDE_MANAGEMENT const struct jmmInterface_1_ jmm_interface = { NULL, NULL, @@ -2235,7 +2263,7 @@ const struct jmmInterface_1_ jmm_interface = { jmm_DumpHeap0, jmm_FindDeadlockedThreads, jmm_SetVMGlobal, - NULL, + jmm_GetTotalThreadAllocatedMemory, jmm_DumpThreads, jmm_SetGCNotificationEnabled, jmm_GetDiagnosticCommands, diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 7a143dbc73e..51db7f13747 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -68,6 +68,8 @@ PerfVariable* ThreadService::_daemon_threads_count = NULL; volatile int ThreadService::_atomic_threads_count = 0; volatile int ThreadService::_atomic_daemon_threads_count = 0; +volatile jlong ThreadService::_exited_allocated_bytes = 0; + ThreadDumpResult* ThreadService::_threaddump_list = NULL; static const int INITIAL_ARRAY_SIZE = 10; @@ -155,6 +157,9 @@ void ThreadService::decrement_thread_counts(JavaThread* jt, bool daemon) { void ThreadService::remove_thread(JavaThread* thread, bool daemon) { assert(Threads_lock->owned_by_self(), "must have threads lock"); + // Include hidden thread allcations in exited_allocated_bytes + ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes()); + // Do not count hidden threads if (is_hidden_thread(thread)) { return; diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp index be16a9684bf..bda5982a0cc 100644 --- a/src/hotspot/share/services/threadService.hpp +++ b/src/hotspot/share/services/threadService.hpp @@ -60,6 +60,10 @@ class ThreadService : public AllStatic { static PerfVariable* _peak_threads_count; static PerfVariable* _daemon_threads_count; + // As could this... + // Number of heap bytes allocated by terminated threads. + static volatile jlong _exited_allocated_bytes; + // These 2 counters are like the above thread counts, but are // atomically decremented in ThreadService::current_thread_exiting instead of // ThreadService::remove_thread, so that the thread count is updated before @@ -98,6 +102,14 @@ class ThreadService : public AllStatic { static jlong get_live_thread_count() { return _atomic_threads_count; } static jlong get_daemon_thread_count() { return _atomic_daemon_threads_count; } + static jlong exited_allocated_bytes() { return Atomic::load(&_exited_allocated_bytes); } + static void incr_exited_allocated_bytes(jlong size) { + // No need for an atomic add because called under the Threads_lock, + // but because _exited_allocated_bytes is read concurrently, need + // atomic store to avoid readers seeing a partial update. + Atomic::store(&_exited_allocated_bytes, _exited_allocated_bytes + size); + } + // Support for thread dump static void add_thread_dump(ThreadDumpResult* dump); static void remove_thread_dump(ThreadDumpResult* dump); diff --git a/src/java.management/share/classes/sun/management/ThreadImpl.java b/src/java.management/share/classes/sun/management/ThreadImpl.java index 8472a66e3f4..ce240af65a1 100644 --- a/src/java.management/share/classes/sun/management/ThreadImpl.java +++ b/src/java.management/share/classes/sun/management/ThreadImpl.java @@ -349,6 +349,13 @@ public void setThreadCpuTimeEnabled(boolean enable) { } } + protected long getTotalThreadAllocatedBytes() { + if (isThreadAllocatedMemoryEnabled()) { + return getTotalThreadAllocatedMemory(); + } + return -1; + } + protected long getCurrentThreadAllocatedBytes() { if (isThreadAllocatedMemoryEnabled()) { return getThreadAllocatedMemory0(0); @@ -532,6 +539,7 @@ private static native void getThreadInfo1(long[] ids, private static native void getThreadUserCpuTime1(long[] ids, long[] result); private static native long getThreadAllocatedMemory0(long id); private static native void getThreadAllocatedMemory1(long[] ids, long[] result); + private static native long getTotalThreadAllocatedMemory(); private static native void setThreadCpuTimeEnabled0(boolean enable); private static native void setThreadAllocatedMemoryEnabled0(boolean enable); private static native void setThreadContentionMonitoringEnabled0(boolean enable); diff --git a/src/java.management/share/native/libmanagement/ThreadImpl.c b/src/java.management/share/native/libmanagement/ThreadImpl.c index d4b151d881b..ec575bb67c7 100644 --- a/src/java.management/share/native/libmanagement/ThreadImpl.c +++ b/src/java.management/share/native/libmanagement/ThreadImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -98,7 +98,7 @@ JNIEXPORT jlong JNICALL Java_sun_management_ThreadImpl_getThreadAllocatedMemory0 (JNIEnv *env, jclass cls, jlong tid) { - return jmm_interface->GetOneThreadAllocatedMemory(env, tid); + return jmm_interface->GetOneThreadAllocatedMemory(env, tid); } JNIEXPORT void JNICALL @@ -108,6 +108,13 @@ Java_sun_management_ThreadImpl_getThreadAllocatedMemory1 jmm_interface->GetThreadAllocatedMemory(env, ids, sizeArray); } +JNIEXPORT jlong JNICALL +Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory + (JNIEnv *env, jclass cls) +{ + return jmm_interface->GetTotalThreadAllocatedMemory(env); +} + JNIEXPORT jobjectArray JNICALL Java_sun_management_ThreadImpl_findMonitorDeadlockedThreads0 (JNIEnv *env, jclass cls) diff --git a/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java b/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java index 7023fab721e..b8518ae26cf 100644 --- a/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java +++ b/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java @@ -107,6 +107,42 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean { */ public long[] getThreadUserTime(long[] ids); + /** + * Returns an approximation of the total amount of memory, in bytes, allocated + * in heap memory by all threads since the Java virtual machine started. + * The returned value is an approximation because some Java virtual machine + * implementations may use object allocation mechanisms that result in a + * delay between the time an object is allocated and the time its size is + * recorded. + * + * @implSpec The default implementation throws {@code UnsupportedOperationException} + * if the Java virtual machine implementation does not support thread + * memory allocation measurement, and otherwise acts as though thread + * memory allocation measurement is disabled. + * + * @return an approximation of the total memory allocated, in bytes, in + * heap memory since the Java virtual machine was started, + * if thread memory allocation measurement is enabled; + * {@code -1} otherwise. + * + * @throws UnsupportedOperationException if the Java virtual + * machine implementation does not support thread memory allocation + * measurement. + * + * @see #isThreadAllocatedMemorySupported + * @see #isThreadAllocatedMemoryEnabled + * @see #setThreadAllocatedMemoryEnabled + * + * @since 17.0.11 + */ + public default long getTotalThreadAllocatedBytes() { + if (!isThreadAllocatedMemorySupported()) { + throw new UnsupportedOperationException( + "Thread allocated memory measurement is not supported."); + } + return -1; + } + /** * Returns an approximation of the total amount of memory, in bytes, * allocated in heap memory for the current thread. diff --git a/src/jdk.management/share/classes/com/sun/management/internal/HotSpotThreadImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotThreadImpl.java index 732ade87fae..16c331242ac 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/HotSpotThreadImpl.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotThreadImpl.java @@ -57,6 +57,11 @@ public long[] getThreadUserTime(long[] ids) { return super.getThreadUserTime(ids); } + @Override + public long getTotalThreadAllocatedBytes() { + return super.getTotalThreadAllocatedBytes(); + } + @Override public long getCurrentThreadAllocatedBytes() { return super.getCurrentThreadAllocatedBytes(); diff --git a/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java b/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java index 9d954f92a85..d54bb06b587 100644 --- a/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java +++ b/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6173675 8231209 + * @bug 6173675 8231209 8304074 * @summary Basic test of ThreadMXBean.getThreadAllocatedBytes * @author Paul Hohensee */ @@ -33,6 +33,7 @@ public class ThreadAllocatedMemory { private static com.sun.management.ThreadMXBean mbean = (com.sun.management.ThreadMXBean)ManagementFactory.getThreadMXBean(); + private static boolean testFailed = false; private static volatile boolean done = false; private static volatile boolean done1 = false; private static Object obj = new Object(); @@ -55,6 +56,13 @@ public static void main(String[] argv) // Test many threads that are not this one testGetThreadsAllocatedBytes(); + // Test cumulative Java thread allocation since JVM launch + testGetTotalThreadAllocatedBytes(); + + if (testFailed) { + throw new RuntimeException("TEST FAILED"); + } + System.out.println("Test passed"); } @@ -92,13 +100,15 @@ private static void testSupportEnableDisable() { } private static void testGetCurrentThreadAllocatedBytes() { + Thread curThread = Thread.currentThread(); + long size = mbean.getCurrentThreadAllocatedBytes(); - ensureValidSize(size); + ensureValidSize(curThread, size); // do some more allocation doit(); - checkResult(Thread.currentThread(), size, + checkResult(curThread, size, mbean.getCurrentThreadAllocatedBytes()); } @@ -107,7 +117,7 @@ private static void testCurrentThreadGetThreadAllocatedBytes() { long id = curThread.getId(); long size = mbean.getThreadAllocatedBytes(id); - ensureValidSize(size); + ensureValidSize(curThread, size); // do some more allocation doit(); @@ -119,7 +129,8 @@ private static void testGetThreadAllocatedBytes() throws Exception { // start a thread - done = false; done1 = false; + done = false; + done1 = false; Thread curThread = new MyThread("MyThread"); curThread.start(); long id = curThread.getId(); @@ -128,7 +139,7 @@ private static void testGetThreadAllocatedBytes() waitUntilThreadBlocked(curThread); long size = mbean.getThreadAllocatedBytes(id); - ensureValidSize(size); + ensureValidSize(curThread, size); // let thread go to do some more allocation synchronized (obj) { @@ -152,8 +163,7 @@ private static void testGetThreadAllocatedBytes() try { curThread.join(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); + reportUnexpected(e, "during join"); } } @@ -161,7 +171,8 @@ private static void testGetThreadsAllocatedBytes() throws Exception { // start threads - done = false; done1 = false; + done = false; + done1 = false; for (int i = 0; i < NUM_THREADS; i++) { threads[i] = new MyThread("MyThread-" + i); threads[i].start(); @@ -172,7 +183,7 @@ private static void testGetThreadsAllocatedBytes() for (int i = 0; i < NUM_THREADS; i++) { sizes[i] = mbean.getThreadAllocatedBytes(threads[i].getId()); - ensureValidSize(sizes[i]); + ensureValidSize(threads[i], sizes[i]); } // let threads go to do some more allocation @@ -201,38 +212,106 @@ private static void testGetThreadsAllocatedBytes() try { threads[i].join(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); + reportUnexpected(e, "during join"); break; } } } - private static void ensureValidSize(long size) { + private static void testGetTotalThreadAllocatedBytes() + throws Exception { + + // baseline should be positive + Thread curThread = Thread.currentThread(); + long cumulativeSize = mbean.getTotalThreadAllocatedBytes(); + if (cumulativeSize <= 0) { + throw new RuntimeException( + "Invalid allocated bytes returned for " + curThread.getName() + " = " + cumulativeSize); + } + + // start threads + done = false; + done1 = false; + for (int i = 0; i < NUM_THREADS; i++) { + threads[i] = new MyThread("MyThread-" + i); + threads[i].start(); + } + + // wait for threads to block after doing some allocation + waitUntilThreadsBlocked(); + + // check after threads are blocked + cumulativeSize = checkResult(curThread, cumulativeSize, mbean.getTotalThreadAllocatedBytes()); + + // let threads go to do some more allocation + synchronized (obj) { + done = true; + obj.notifyAll(); + } + + // wait for threads to get going again. we don't care if we + // catch them in mid-execution or if some of them haven't + // restarted after we're done sleeping. + goSleep(400); + + System.out.println("Done sleeping"); + + // check while threads are running + cumulativeSize = checkResult(curThread, cumulativeSize, mbean.getTotalThreadAllocatedBytes()); + + // let threads exit + synchronized (obj) { + done1 = true; + obj.notifyAll(); + } + + for (int i = 0; i < NUM_THREADS; i++) { + try { + threads[i].join(); + } catch (InterruptedException e) { + reportUnexpected(e, "during join"); + break; + } + } + + // check after threads exit + checkResult(curThread, cumulativeSize, mbean.getTotalThreadAllocatedBytes()); + } + + private static void ensureValidSize(Thread curThread, long size) { // implementation could have started measurement when // measurement was enabled, in which case size can be 0 if (size < 0) { throw new RuntimeException( - "Invalid allocated bytes returned = " + size); + "Invalid allocated bytes returned for thread " + + curThread.getName() + " = " + size); } } - private static void checkResult(Thread curThread, - long prev_size, long curr_size) { - if (curr_size < prev_size) { - throw new RuntimeException("Allocated bytes " + curr_size + - " expected >= " + prev_size); - } + private static long checkResult(Thread curThread, + long prevSize, long currSize) { System.out.println(curThread.getName() + - " Previous allocated bytes = " + prev_size + - " Current allocated bytes = " + curr_size); + " Previous allocated bytes = " + prevSize + + " Current allocated bytes = " + currSize); + if (currSize < prevSize) { + throw new RuntimeException("TEST FAILED: " + + curThread.getName() + + " previous allocated bytes = " + prevSize + + " > current allocated bytes = " + currSize); + } + return currSize; + } + + private static void reportUnexpected(Exception e, String when) { + System.out.println("Unexpected exception thrown " + when + "."); + e.printStackTrace(System.out); + testFailed = true; } private static void goSleep(long ms) throws Exception { try { Thread.sleep(ms); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); throw e; } } @@ -287,34 +366,23 @@ public void run() { try { obj.wait(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); + reportUnexpected(e, "while !done"); break; } } } - long size1 = mbean.getThreadAllocatedBytes(getId()); + long prevSize = mbean.getThreadAllocatedBytes(getId()); ThreadAllocatedMemory.doit(); - long size2 = mbean.getThreadAllocatedBytes(getId()); - - System.out.println(getName() + ": " + - "ThreadAllocatedBytes = " + size1 + - " ThreadAllocatedBytes = " + size2); - - if (size1 > size2) { - throw new RuntimeException(getName() + - " ThreadAllocatedBytes = " + size1 + - " > ThreadAllocatedBytes = " + size2); - } + long currSize = mbean.getThreadAllocatedBytes(getId()); + checkResult(this, prevSize, currSize); synchronized (obj) { while (!done1) { try { obj.wait(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); + reportUnexpected(e, "while !done1"); break; } } diff --git a/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java b/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java index cbd3d1d3cab..d325d1dda66 100644 --- a/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java +++ b/test/jdk/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, 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 @@ -47,7 +47,6 @@ public static void main(String[] argv) return; } - // start threads, wait for them to block long[] ids = new long[NUM_THREADS]; @@ -59,7 +58,6 @@ public static void main(String[] argv) waitUntilThreadBlocked(); - // disable allocated memory measurement if (mbean.isThreadAllocatedMemoryEnabled()) { mbean.setThreadAllocatedMemoryEnabled(false); @@ -117,19 +115,9 @@ public static void main(String[] argv) // restarted after we're done sleeping. goSleep(400); - long[] sizes1 = mbean.getThreadAllocatedBytes(ids); - + long[] afterSizes = mbean.getThreadAllocatedBytes(ids); for (int i = 0; i < NUM_THREADS; i++) { - long newSize = sizes1[i]; - if (sizes[i] > newSize) { - throw new RuntimeException("TEST FAILED: " + - threads[i].getName() + - " previous allocated bytes = " + sizes[i] + - " > current allocated bytes = " + newSize); - } - System.out.println(threads[i].getName() + - " Previous allocated bytes = " + sizes[i] + - " Current allocated bytes = " + newSize); + checkResult(threads[i], sizes[i], afterSizes[i]); } try { @@ -147,7 +135,6 @@ public static void main(String[] argv) "Caught expected IllegalArgumentException: " + e.getMessage()); } - // let threads exit synchronized (obj) { done1 = true; @@ -158,9 +145,7 @@ public static void main(String[] argv) try { threads[i].join(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); - testFailed = true; + reportUnexpected(e, "during join"); break; } } @@ -173,11 +158,30 @@ public static void main(String[] argv) } + private static void checkResult(Thread curThread, + long prevSize, long currSize) { + System.out.println(curThread.getName() + + " Previous allocated bytes = " + prevSize + + " Current allocated bytes = " + currSize); + if (currSize < prevSize) { + throw new RuntimeException("TEST FAILED: " + + curThread.getName() + + " previous allocated bytes = " + prevSize + + " > current allocated bytes = " + currSize); + + } + } + + private static void reportUnexpected(Exception e, String when) { + System.out.println("Unexpected exception thrown " + when + "."); + e.printStackTrace(System.out); + testFailed = true; + } + private static void goSleep(long ms) throws Exception { try { Thread.sleep(ms); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); throw e; } } @@ -221,9 +225,7 @@ public void run() { try { obj.wait(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); - testFailed = true; + reportUnexpected(e, "while !done"); break; } } @@ -236,9 +238,7 @@ public void run() { try { obj.wait(); } catch (InterruptedException e) { - System.out.println("Unexpected exception is thrown."); - e.printStackTrace(System.out); - testFailed = true; + reportUnexpected(e, "while !done"); break; } }