Skip to content

Commit 3eced01

Browse files
author
Paul Hohensee
committed
8304074: [JMX] Add an approximation of total bytes allocated on the Java heap by the JVM
Reviewed-by: dholmes, mchung
1 parent 15e0285 commit 3eced01

File tree

11 files changed

+253
-79
lines changed

11 files changed

+253
-79
lines changed

src/hotspot/share/include/jmm.h

Lines changed: 5 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
@@ -52,7 +52,8 @@ enum {
5252
JMM_VERSION_1_2_2 = 0x20010202,
5353
JMM_VERSION_2 = 0x20020000, // JDK 10
5454
JMM_VERSION_3 = 0x20030000, // JDK 14
55-
JMM_VERSION = JMM_VERSION_3
55+
JMM_VERSION_4 = 0x20040000, // JDK 21
56+
JMM_VERSION = JMM_VERSION_4
5657
};
5758

5859
typedef struct {
@@ -240,6 +241,8 @@ typedef struct jmmInterface_1_ {
240241
jobject (JNICALL *GetMemoryPoolUsage) (JNIEnv* env, jobject pool);
241242
jobject (JNICALL *GetPeakMemoryPoolUsage) (JNIEnv* env, jobject pool);
242243

244+
jlong (JNICALL *GetTotalThreadAllocatedMemory)
245+
(JNIEnv *env);
243246
jlong (JNICALL *GetOneThreadAllocatedMemory)
244247
(JNIEnv *env,
245248
jlong thread_id);

src/hotspot/share/runtime/mutexLocker.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel
7474
extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats
7575
extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list.
7676
extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
77-
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools.
77+
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark.
7878
extern Monitor* ConcurrentGCBreakpoints_lock; // Protects concurrent GC breakpoint management
7979
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
8080
extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued

src/hotspot/share/services/management.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "runtime/interfaceSupport.inline.hpp"
4848
#include "runtime/javaCalls.hpp"
4949
#include "runtime/jniHandles.inline.hpp"
50+
#include "runtime/mutexLocker.hpp"
5051
#include "runtime/notificationThread.hpp"
5152
#include "runtime/os.hpp"
5253
#include "runtime/thread.inline.hpp"
@@ -428,8 +429,6 @@ static MemoryPool* get_memory_pool_from_jobject(jobject obj, TRAPS) {
428429
return MemoryService::get_memory_pool(ph);
429430
}
430431

431-
#endif // INCLUDE_MANAGEMENT
432-
433432
static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
434433
int num_threads = ids_ah->length();
435434

@@ -455,8 +454,6 @@ static bool is_platform_thread(JavaThread* jt) {
455454
}
456455
}
457456

458-
#if INCLUDE_MANAGEMENT
459-
460457
static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
461458
// check if the element of infoArray is of type ThreadInfo class
462459
Klass* threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK);
@@ -2095,7 +2092,41 @@ jlong Management::ticks_to_ms(jlong ticks) {
20952092
return (jlong)(((double)ticks / (double)os::elapsed_frequency())
20962093
* (double)1000.0);
20972094
}
2098-
#endif // INCLUDE_MANAGEMENT
2095+
2096+
// Gets the amount of memory allocated on the Java heap since JVM launch.
2097+
JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env))
2098+
// A thread increments exited_allocated_bytes in ThreadService::remove_thread
2099+
// only after it removes itself from the threads list, and once a TLH is
2100+
// created, no thread it references can remove itself from the threads
2101+
// list, so none can update exited_allocated_bytes. We therefore initialize
2102+
// result with exited_allocated_bytes after after we create the TLH so that
2103+
// the final result can only be short due to (1) threads that start after
2104+
// the TLH is created, or (2) terminating threads that escape TLH creation
2105+
// and don't update exited_allocated_bytes before we initialize result.
2106+
2107+
// We keep a high water mark to ensure monotonicity in case threads counted
2108+
// on a previous call end up in state (2).
2109+
static jlong high_water_result = 0;
2110+
2111+
JavaThreadIteratorWithHandle jtiwh;
2112+
jlong result = ThreadService::exited_allocated_bytes();
2113+
for (; JavaThread* thread = jtiwh.next();) {
2114+
jlong size = thread->cooked_allocated_bytes();
2115+
result += size;
2116+
}
2117+
2118+
{
2119+
MutexLocker ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag);
2120+
if (result < high_water_result) {
2121+
// Encountered (2) above, or result wrapped to a negative value. In
2122+
// the latter case, it's pegged at the last positive value.
2123+
result = high_water_result;
2124+
} else {
2125+
high_water_result = result;
2126+
}
2127+
}
2128+
return result;
2129+
JVM_END
20992130

21002131
// Gets the amount of memory allocated on the Java heap for a single thread.
21012132
// Returns -1 if the thread does not exist or has terminated.
@@ -2228,9 +2259,6 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
22282259
}
22292260
JVM_END
22302261

2231-
2232-
2233-
#if INCLUDE_MANAGEMENT
22342262
const struct jmmInterface_1_ jmm_interface = {
22352263
nullptr,
22362264
nullptr,
@@ -2241,6 +2269,7 @@ const struct jmmInterface_1_ jmm_interface = {
22412269
jmm_GetMemoryManagers,
22422270
jmm_GetMemoryPoolUsage,
22432271
jmm_GetPeakMemoryPoolUsage,
2272+
jmm_GetTotalThreadAllocatedMemory,
22442273
jmm_GetOneThreadAllocatedMemory,
22452274
jmm_GetThreadAllocatedMemory,
22462275
jmm_GetMemoryUsage,

src/hotspot/share/services/threadService.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "runtime/init.hpp"
4646
#include "runtime/javaThread.inline.hpp"
4747
#include "runtime/objectMonitor.inline.hpp"
48+
#include "runtime/thread.inline.hpp"
4849
#include "runtime/threads.hpp"
4950
#include "runtime/threadSMR.inline.hpp"
5051
#include "runtime/vframe.hpp"
@@ -70,6 +71,8 @@ PerfVariable* ThreadService::_daemon_threads_count = nullptr;
7071
volatile int ThreadService::_atomic_threads_count = 0;
7172
volatile int ThreadService::_atomic_daemon_threads_count = 0;
7273

74+
volatile jlong ThreadService::_exited_allocated_bytes = 0;
75+
7376
ThreadDumpResult* ThreadService::_threaddump_list = nullptr;
7477

7578
static const int INITIAL_ARRAY_SIZE = 10;
@@ -157,6 +160,9 @@ void ThreadService::decrement_thread_counts(JavaThread* jt, bool daemon) {
157160
void ThreadService::remove_thread(JavaThread* thread, bool daemon) {
158161
assert(Threads_lock->owned_by_self(), "must have threads lock");
159162

163+
// Include hidden thread allcations in exited_allocated_bytes
164+
ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes());
165+
160166
// Do not count hidden threads
161167
if (is_hidden_thread(thread)) {
162168
return;

src/hotspot/share/services/threadService.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ class ThreadService : public AllStatic {
6161
static PerfVariable* _peak_threads_count;
6262
static PerfVariable* _daemon_threads_count;
6363

64+
// As could this...
65+
// Number of heap bytes allocated by terminated threads.
66+
static volatile jlong _exited_allocated_bytes;
67+
6468
// These 2 counters are like the above thread counts, but are
6569
// atomically decremented in ThreadService::current_thread_exiting instead of
6670
// ThreadService::remove_thread, so that the thread count is updated before
@@ -102,6 +106,14 @@ class ThreadService : public AllStatic {
102106
static jlong get_live_thread_count() { return _atomic_threads_count; }
103107
static jlong get_daemon_thread_count() { return _atomic_daemon_threads_count; }
104108

109+
static jlong exited_allocated_bytes() { return Atomic::load(&_exited_allocated_bytes); }
110+
static void incr_exited_allocated_bytes(jlong size) {
111+
// No need for an atomic add because called under the Threads_lock,
112+
// but because _exited_allocated_bytes is read concurrently, need
113+
// atomic store to avoid readers seeing a partial update.
114+
Atomic::store(&_exited_allocated_bytes, _exited_allocated_bytes + size);
115+
}
116+
105117
// Support for thread dump
106118
static void add_thread_dump(ThreadDumpResult* dump);
107119
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
@@ -342,6 +342,13 @@ public void setThreadCpuTimeEnabled(boolean enable) {
342342
}
343343
}
344344

345+
protected long getTotalThreadAllocatedBytes() {
346+
if (isThreadAllocatedMemoryEnabled()) {
347+
return getTotalThreadAllocatedMemory();
348+
}
349+
return -1;
350+
}
351+
345352
protected long getCurrentThreadAllocatedBytes() {
346353
if (isThreadAllocatedMemoryEnabled() && !Thread.currentThread().isVirtual()) {
347354
return getThreadAllocatedMemory0(0);
@@ -525,6 +532,7 @@ private static native void getThreadInfo1(long[] ids,
525532
private static native void getThreadUserCpuTime1(long[] ids, long[] result);
526533
private static native long getThreadAllocatedMemory0(long id);
527534
private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
535+
private static native long getTotalThreadAllocatedMemory();
528536
private static native void setThreadCpuTimeEnabled0(boolean enable);
529537
private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
530538
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
@@ -105,6 +105,42 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
105105
*/
106106
public long[] getThreadUserTime(long[] ids);
107107

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