Skip to content

Commit 31064b1

Browse files
author
Paul Hohensee
committed
8304074: [JMX] Add an approximation of total bytes allocated on the Java heap by the JVM
Reviewed-by: simonis Backport-of: 3eced01f9efe2567a07b63343f8559683a2d0517
1 parent 8a390c8 commit 31064b1

File tree

12 files changed

+254
-80
lines changed

12 files changed

+254
-80
lines changed

src/hotspot/share/include/jmm.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -314,7 +314,8 @@ typedef struct jmmInterface_1_ {
314314
void (JNICALL *SetVMGlobal) (JNIEnv *env,
315315
jstring flag_name,
316316
jvalue new_value);
317-
void* reserved6;
317+
jlong (JNICALL *GetTotalThreadAllocatedMemory)
318+
(JNIEnv *env);
318319
jobjectArray (JNICALL *DumpThreads) (JNIEnv *env,
319320
jlongArray ids,
320321
jboolean lockedMonitors,

src/hotspot/share/runtime/mutexLocker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Monitor* DirtyCardQ_CBL_mon = NULL;
8888
Mutex* Shared_DirtyCardQ_lock = NULL;
8989
Mutex* MarkStackFreeList_lock = NULL;
9090
Mutex* MarkStackChunkList_lock = NULL;
91+
Mutex* MonitoringSupport_lock = NULL;
9192
Mutex* ParGCRareEvent_lock = NULL;
9293
Mutex* DerivedPointerTableGC_lock = NULL;
9394
Monitor* CGCPhaseManager_lock = NULL;
@@ -224,6 +225,8 @@ void mutex_init() {
224225

225226
def(MarkStackFreeList_lock , PaddedMutex , leaf , true, Monitor::_safepoint_check_never);
226227
def(MarkStackChunkList_lock , PaddedMutex , leaf , true, Monitor::_safepoint_check_never);
228+
229+
def(MonitoringSupport_lock , PaddedMutex , leaf , true, Monitor::_safepoint_check_never); // used for serviceability monitoring support
227230
}
228231
#if INCLUDE_SHENANDOAHGC
229232
if (UseShenandoahGC) {

src/hotspot/share/runtime/mutexLocker.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -94,6 +94,7 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card
9494
// non-Java threads.
9595
extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list.
9696
extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
97+
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark.
9798
extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops.
9899
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
99100
extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued

src/hotspot/share/services/management.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "runtime/interfaceSupport.inline.hpp"
4343
#include "runtime/javaCalls.hpp"
4444
#include "runtime/jniHandles.inline.hpp"
45+
#include "runtime/mutexLocker.hpp"
4546
#include "runtime/os.hpp"
4647
#include "runtime/thread.inline.hpp"
4748
#include "runtime/threadSMR.hpp"
@@ -408,8 +409,6 @@ static MemoryPool* get_memory_pool_from_jobject(jobject obj, TRAPS) {
408409
return MemoryService::get_memory_pool(ph);
409410
}
410411

411-
#endif // INCLUDE_MANAGEMENT
412-
413412
static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
414413
int num_threads = ids_ah->length();
415414

@@ -425,8 +424,6 @@ static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
425424
}
426425
}
427426

428-
#if INCLUDE_MANAGEMENT
429-
430427
static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
431428
// check if the element of infoArray is of type ThreadInfo class
432429
Klass* threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK);
@@ -2059,7 +2056,41 @@ jlong Management::ticks_to_ms(jlong ticks) {
20592056
return (jlong)(((double)ticks / (double)os::elapsed_frequency())
20602057
* (double)1000.0);
20612058
}
2062-
#endif // INCLUDE_MANAGEMENT
2059+
2060+
// Gets the amount of memory allocated on the Java heap since JVM launch.
2061+
JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env))
2062+
// A thread increments exited_allocated_bytes in ThreadService::remove_thread
2063+
// only after it removes itself from the threads list, and once a TLH is
2064+
// created, no thread it references can remove itself from the threads
2065+
// list, so none can update exited_allocated_bytes. We therefore initialize
2066+
// result with exited_allocated_bytes after after we create the TLH so that
2067+
// the final result can only be short due to (1) threads that start after
2068+
// the TLH is created, or (2) terminating threads that escape TLH creation
2069+
// and don't update exited_allocated_bytes before we initialize result.
2070+
2071+
// We keep a high water mark to ensure monotonicity in case threads counted
2072+
// on a previous call end up in state (2).
2073+
static jlong high_water_result = 0;
2074+
2075+
JavaThreadIteratorWithHandle jtiwh;
2076+
jlong result = ThreadService::exited_allocated_bytes();
2077+
for (; JavaThread* thread = jtiwh.next();) {
2078+
jlong size = thread->cooked_allocated_bytes();
2079+
result += size;
2080+
}
2081+
2082+
{
2083+
MutexLockerEx ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag);
2084+
if (result < high_water_result) {
2085+
// Encountered (2) above, or result wrapped to a negative value. In
2086+
// the latter case, it's pegged at the last positive value.
2087+
result = high_water_result;
2088+
} else {
2089+
high_water_result = result;
2090+
}
2091+
}
2092+
return result;
2093+
JVM_END
20632094

20642095
// Gets the amount of memory allocated on the Java heap for a single thread.
20652096
// Returns -1 if the thread does not exist or has terminated.
@@ -2197,9 +2228,6 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
21972228
}
21982229
JVM_END
21992230

2200-
2201-
2202-
#if INCLUDE_MANAGEMENT
22032231
const struct jmmInterface_1_ jmm_interface = {
22042232
NULL,
22052233
jmm_GetOneThreadAllocatedMemory,
@@ -2232,7 +2260,7 @@ const struct jmmInterface_1_ jmm_interface = {
22322260
jmm_DumpHeap0,
22332261
jmm_FindDeadlockedThreads,
22342262
jmm_SetVMGlobal,
2235-
NULL,
2263+
jmm_GetTotalThreadAllocatedMemory,
22362264
jmm_DumpThreads,
22372265
jmm_SetGCNotificationEnabled,
22382266
jmm_GetDiagnosticCommands,

src/hotspot/share/services/threadService.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ PerfVariable* ThreadService::_daemon_threads_count = NULL;
6060
volatile int ThreadService::_atomic_threads_count = 0;
6161
volatile int ThreadService::_atomic_daemon_threads_count = 0;
6262

63+
volatile jlong ThreadService::_exited_allocated_bytes = 0;
64+
6365
ThreadDumpResult* ThreadService::_threaddump_list = NULL;
6466

6567
static const int INITIAL_ARRAY_SIZE = 10;
@@ -140,6 +142,9 @@ void ThreadService::decrement_thread_counts(JavaThread* jt, bool daemon) {
140142
void ThreadService::remove_thread(JavaThread* thread, bool daemon) {
141143
assert(Threads_lock->owned_by_self(), "must have threads lock");
142144

145+
// Include hidden thread allcations in exited_allocated_bytes
146+
ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes());
147+
143148
// Do not count hidden threads
144149
if (is_hidden_thread(thread)) {
145150
return;

src/hotspot/share/services/threadService.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class ThreadService : public AllStatic {
5858
static PerfVariable* _peak_threads_count;
5959
static PerfVariable* _daemon_threads_count;
6060

61+
// As could this...
62+
// Number of heap bytes allocated by terminated threads.
63+
static volatile jlong _exited_allocated_bytes;
64+
6165
// These 2 counters are like the above thread counts, but are
6266
// atomically decremented in ThreadService::current_thread_exiting instead of
6367
// ThreadService::remove_thread, so that the thread count is updated before
@@ -96,6 +100,14 @@ class ThreadService : public AllStatic {
96100
static jlong get_live_thread_count() { return _atomic_threads_count; }
97101
static jlong get_daemon_thread_count() { return _atomic_daemon_threads_count; }
98102

103+
static jlong exited_allocated_bytes() { return Atomic::load(&_exited_allocated_bytes); }
104+
static void incr_exited_allocated_bytes(jlong size) {
105+
// No need for an atomic add because called under the Threads_lock,
106+
// but because _exited_allocated_bytes is read concurrently, need
107+
// atomic store to avoid readers seeing a partial update.
108+
Atomic::store(_exited_allocated_bytes + size, &_exited_allocated_bytes);
109+
}
110+
99111
// Support for thread dump
100112
static void add_thread_dump(ThreadDumpResult* dump);
101113
static void remove_thread_dump(ThreadDumpResult* dump);

src/java.management/share/classes/sun/management/ThreadImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,13 @@ public void setThreadCpuTimeEnabled(boolean enable) {
349349
}
350350
}
351351

352+
protected long getTotalThreadAllocatedBytes() {
353+
if (isThreadAllocatedMemoryEnabled()) {
354+
return getTotalThreadAllocatedMemory();
355+
}
356+
return -1;
357+
}
358+
352359
protected long getCurrentThreadAllocatedBytes() {
353360
if (isThreadAllocatedMemoryEnabled()) {
354361
return getThreadAllocatedMemory0(0);
@@ -532,6 +539,7 @@ private static native void getThreadInfo1(long[] ids,
532539
private static native void getThreadUserCpuTime1(long[] ids, long[] result);
533540
private static native long getThreadAllocatedMemory0(long id);
534541
private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
542+
private static native long getTotalThreadAllocatedMemory();
535543
private static native void setThreadCpuTimeEnabled0(boolean enable);
536544
private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
537545
private static native void setThreadContentionMonitoringEnabled0(boolean enable);

src/java.management/share/native/libmanagement/ThreadImpl.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -98,7 +98,7 @@ JNIEXPORT jlong JNICALL
9898
Java_sun_management_ThreadImpl_getThreadAllocatedMemory0
9999
(JNIEnv *env, jclass cls, jlong tid)
100100
{
101-
return jmm_interface->GetOneThreadAllocatedMemory(env, tid);
101+
return jmm_interface->GetOneThreadAllocatedMemory(env, tid);
102102
}
103103

104104
JNIEXPORT void JNICALL
@@ -108,6 +108,13 @@ Java_sun_management_ThreadImpl_getThreadAllocatedMemory1
108108
jmm_interface->GetThreadAllocatedMemory(env, ids, sizeArray);
109109
}
110110

111+
JNIEXPORT jlong JNICALL
112+
Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory
113+
(JNIEnv *env, jclass cls)
114+
{
115+
return jmm_interface->GetTotalThreadAllocatedMemory(env);
116+
}
117+
111118
JNIEXPORT jobjectArray JNICALL
112119
Java_sun_management_ThreadImpl_findMonitorDeadlockedThreads0
113120
(JNIEnv *env, jclass cls)

src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,42 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
107107
*/
108108
public long[] getThreadUserTime(long[] ids);
109109

110+
/**
111+
* Returns an approximation of the total amount of memory, in bytes, allocated
112+
* in heap memory by all threads since the Java virtual machine started.
113+
* The returned value is an approximation because some Java virtual machine
114+
* implementations may use object allocation mechanisms that result in a
115+
* delay between the time an object is allocated and the time its size is
116+
* recorded.
117+
*
118+
* @implSpec The default implementation throws {@code UnsupportedOperationException}
119+
* if the Java virtual machine implementation does not support thread
120+
* memory allocation measurement, and otherwise acts as though thread
121+
* memory allocation measurement is disabled.
122+
*
123+
* @return an approximation of the total memory allocated, in bytes, in
124+
* heap memory since the Java virtual machine was started,
125+
* if thread memory allocation measurement is enabled;
126+
* {@code -1} otherwise.
127+
*
128+
* @throws UnsupportedOperationException if the Java virtual
129+
* machine implementation does not support thread memory allocation
130+
* measurement.
131+
*
132+
* @see #isThreadAllocatedMemorySupported
133+
* @see #isThreadAllocatedMemoryEnabled
134+
* @see #setThreadAllocatedMemoryEnabled
135+
*
136+
* @since 11.0.23
137+
*/
138+
public default long getTotalThreadAllocatedBytes() {
139+
if (!isThreadAllocatedMemorySupported()) {
140+
throw new UnsupportedOperationException(
141+
"Thread allocated memory measurement is not supported.");
142+
}
143+
return -1;
144+
}
145+
110146
/**
111147
* Returns an approximation of the total amount of memory, in bytes,
112148
* allocated in heap memory for the current thread.

src/jdk.management/share/classes/com/sun/management/internal/HotSpotThreadImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public long[] getThreadUserTime(long[] ids) {
5757
return super.getThreadUserTime(ids);
5858
}
5959

60+
@Override
61+
public long getTotalThreadAllocatedBytes() {
62+
return super.getTotalThreadAllocatedBytes();
63+
}
64+
6065
@Override
6166
public long getCurrentThreadAllocatedBytes() {
6267
return super.getCurrentThreadAllocatedBytes();

0 commit comments

Comments
 (0)