diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp index 2dcda097ebb..2950b0aa2ef 100644 --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp @@ -81,6 +81,7 @@ Mutex* Shared_SATB_Q_lock = NULL; Mutex* DirtyCardQ_FL_lock = NULL; Monitor* DirtyCardQ_CBL_mon = NULL; Mutex* Shared_DirtyCardQ_lock = NULL; +Mutex* MonitoringSupport_lock = NULL; Mutex* ParGCRareEvent_lock = NULL; Mutex* EvacFailureStack_lock = NULL; Mutex* DerivedPointerTableGC_lock = NULL; @@ -208,6 +209,8 @@ void mutex_init() { def(StringDedupQueue_lock , Monitor, leaf, true ); def(StringDedupTable_lock , Mutex , leaf, true ); + + def(MonitoringSupport_lock , Mutex , leaf, true ); // used for serviceability monitoring support } def(ParGCRareEvent_lock , Mutex , leaf , true ); def(DerivedPointerTableGC_lock , Mutex, leaf, true ); diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp index ec642a24e65..098cb9f443f 100644 --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -101,6 +101,7 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card // queue shared by // non-Java threads. // (see option ExplicitGCInvokesConcurrent) +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 Mutex* EvacFailureStack_lock; // guards the evac failure scan stack extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc) diff --git a/hotspot/src/share/vm/services/jmm.h b/hotspot/src/share/vm/services/jmm.h index e00b882dd1d..00adb4ad584 100644 --- a/hotspot/src/share/vm/services/jmm.h +++ b/hotspot/src/share/vm/services/jmm.h @@ -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 @@ -218,7 +218,8 @@ typedef struct { } dcmdArgInfo; typedef struct jmmInterface_1_ { - void* reserved1; + jlong (JNICALL *GetTotalThreadAllocatedMemory) + (JNIEnv *env); jlong (JNICALL *GetOneThreadAllocatedMemory) (JNIEnv *env, jlong thread_id); diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index 6163ac35dac..566e7a780a9 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -37,6 +37,7 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "runtime/serviceThread.hpp" #include "runtime/thread.inline.hpp" @@ -428,8 +429,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(); @@ -445,8 +444,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); @@ -2230,7 +2227,39 @@ 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)) + // We keep a high water mark to ensure monotonicity + static jlong high_water_result = 0; + static jlong prev_result = 0; + + jlong result; + if (Threads_lock->try_lock()) { + result = ThreadService::exited_allocated_bytes(); + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + jlong size = thread->cooked_allocated_bytes(); + result += size; + } + Threads_lock->unlock(); + } else { + // Return the previous result if Threads_lock is locked + result = prev_result; + } + + { + MutexLockerEx ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag); + if (result < high_water_result) { + // Result wrapped to a negative value, in which case it's + // pegged at the last positive value. + result = high_water_result; + } else { + high_water_result = result; + } + prev_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. @@ -2368,11 +2397,8 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids, } JVM_END - - -#if INCLUDE_MANAGEMENT const struct jmmInterface_1_ jmm_interface = { - NULL, + jmm_GetTotalThreadAllocatedMemory, jmm_GetOneThreadAllocatedMemory, jmm_GetVersion, jmm_GetOptionalSupport, diff --git a/hotspot/src/share/vm/services/threadService.cpp b/hotspot/src/share/vm/services/threadService.cpp index 93ba0579fad..3bfd6b538de 100644 --- a/hotspot/src/share/vm/services/threadService.cpp +++ b/hotspot/src/share/vm/services/threadService.cpp @@ -58,6 +58,8 @@ PerfVariable* ThreadService::_daemon_threads_count = NULL; volatile int ThreadService::_exiting_threads_count = 0; volatile int ThreadService::_exiting_daemon_threads_count = 0; +volatile jlong ThreadService::_exited_allocated_bytes = 0; + ThreadDumpResult* ThreadService::_threaddump_list = NULL; static const int INITIAL_ARRAY_SIZE = 10; @@ -119,6 +121,9 @@ void ThreadService::add_thread(JavaThread* thread, bool daemon) { } void ThreadService::remove_thread(JavaThread* thread, bool daemon) { + // Include hidden thread allcations in exited_allocated_bytes + ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes()); + Atomic::dec((jint*) &_exiting_threads_count); if (thread->is_hidden_from_external_view() || diff --git a/hotspot/src/share/vm/services/threadService.hpp b/hotspot/src/share/vm/services/threadService.hpp index 46bc012f73e..5c725db60c8 100644 --- a/hotspot/src/share/vm/services/threadService.hpp +++ b/hotspot/src/share/vm/services/threadService.hpp @@ -58,6 +58,8 @@ class ThreadService : public AllStatic { static PerfVariable* _peak_threads_count; static PerfVariable* _daemon_threads_count; + static volatile jlong _exited_allocated_bytes; + // These 2 counters are atomically incremented once the thread is exiting. // They will be atomically decremented when ThreadService::remove_thread is called. static volatile int _exiting_threads_count; @@ -95,6 +97,14 @@ class ThreadService : public AllStatic { static int exiting_threads_count() { return _exiting_threads_count; } static int exiting_daemon_threads_count() { return _exiting_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 + size, &_exited_allocated_bytes); + } + // Support for thread dump static void add_thread_dump(ThreadDumpResult* dump); static void remove_thread_dump(ThreadDumpResult* dump); diff --git a/jdk/make/mapfiles/libmanagement/mapfile-vers b/jdk/make/mapfiles/libmanagement/mapfile-vers index 7ef8f3ad514..1e1f52578df 100644 --- a/jdk/make/mapfiles/libmanagement/mapfile-vers +++ b/jdk/make/mapfiles/libmanagement/mapfile-vers @@ -89,6 +89,7 @@ SUNWprivate_1.1 { Java_sun_management_ThreadImpl_getThreadUserCpuTime1; Java_sun_management_ThreadImpl_getThreadAllocatedMemory0; Java_sun_management_ThreadImpl_getThreadAllocatedMemory1; + Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory; Java_sun_management_ThreadImpl_resetContentionTimes0; Java_sun_management_ThreadImpl_resetPeakThreadCount0; Java_sun_management_ThreadImpl_setThreadContentionMonitoringEnabled0; diff --git a/jdk/src/share/classes/com/sun/management/ThreadMXBean.java b/jdk/src/share/classes/com/sun/management/ThreadMXBean.java index b1868d5e592..10091860a56 100644 --- a/jdk/src/share/classes/com/sun/management/ThreadMXBean.java +++ b/jdk/src/share/classes/com/sun/management/ThreadMXBean.java @@ -109,6 +109,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 8u412 + */ + 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/jdk/src/share/classes/sun/management/ThreadImpl.java b/jdk/src/share/classes/sun/management/ThreadImpl.java index 4d25d7daddb..612821552a5 100644 --- a/jdk/src/share/classes/sun/management/ThreadImpl.java +++ b/jdk/src/share/classes/sun/management/ThreadImpl.java @@ -331,6 +331,13 @@ public void setThreadCpuTimeEnabled(boolean enable) { } } + public long getTotalThreadAllocatedBytes() { + if (isThreadAllocatedMemoryEnabled()) { + return getTotalThreadAllocatedMemory(); + } + return -1; + } + public long getCurrentThreadAllocatedBytes() { if (isThreadAllocatedMemoryEnabled()) { return getThreadAllocatedMemory0(0); @@ -507,6 +514,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/jdk/src/share/javavm/export/jmm.h b/jdk/src/share/javavm/export/jmm.h index 23d8fa4061a..508b4c8cb1d 100644 --- a/jdk/src/share/javavm/export/jmm.h +++ b/jdk/src/share/javavm/export/jmm.h @@ -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 @@ -218,7 +218,8 @@ typedef struct { } dcmdArgInfo; typedef struct jmmInterface_1_ { - void* reserved1; + jlong (JNICALL *GetTotalThreadAllocatedMemory) + (JNIEnv *env); jlong (JNICALL *GetOneThreadAllocatedMemory) (JNIEnv *env, jlong thread_id); diff --git a/jdk/src/share/native/sun/management/ThreadImpl.c b/jdk/src/share/native/sun/management/ThreadImpl.c index b696f2f7de9..c92ec381d10 100644 --- a/jdk/src/share/native/sun/management/ThreadImpl.c +++ b/jdk/src/share/native/sun/management/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/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java b/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java index 9d954f92a85..d54bb06b587 100644 --- a/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemory.java +++ b/jdk/test/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/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java b/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java index 980189098cb..d325d1dda66 100644 --- a/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java +++ b/jdk/test/com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 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; } }