Skip to content

Commit 1fad52d

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, andrew Backport-of: 3eced01f9efe2567a07b63343f8559683a2d0517
1 parent d7ad5bf commit 1fad52d

File tree

13 files changed

+249
-82
lines changed

13 files changed

+249
-82
lines changed

hotspot/src/share/vm/runtime/mutexLocker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Mutex* Shared_SATB_Q_lock = NULL;
8181
Mutex* DirtyCardQ_FL_lock = NULL;
8282
Monitor* DirtyCardQ_CBL_mon = NULL;
8383
Mutex* Shared_DirtyCardQ_lock = NULL;
84+
Mutex* MonitoringSupport_lock = NULL;
8485
Mutex* ParGCRareEvent_lock = NULL;
8586
Mutex* EvacFailureStack_lock = NULL;
8687
Mutex* DerivedPointerTableGC_lock = NULL;
@@ -208,6 +209,8 @@ void mutex_init() {
208209

209210
def(StringDedupQueue_lock , Monitor, leaf, true );
210211
def(StringDedupTable_lock , Mutex , leaf, true );
212+
213+
def(MonitoringSupport_lock , Mutex , leaf, true ); // used for serviceability monitoring support
211214
}
212215
def(ParGCRareEvent_lock , Mutex , leaf , true );
213216
def(DerivedPointerTableGC_lock , Mutex, leaf, true );

hotspot/src/share/vm/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, 2015, 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
@@ -101,6 +101,7 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card
101101
// queue shared by
102102
// non-Java threads.
103103
// (see option ExplicitGCInvokesConcurrent)
104+
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark.
104105
extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops.
105106
extern Mutex* EvacFailureStack_lock; // guards the evac failure scan stack
106107
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)

hotspot/src/share/vm/services/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, 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
@@ -218,7 +218,8 @@ typedef struct {
218218
} dcmdArgInfo;
219219

220220
typedef struct jmmInterface_1_ {
221-
void* reserved1;
221+
jlong (JNICALL *GetTotalThreadAllocatedMemory)
222+
(JNIEnv *env);
222223
jlong (JNICALL *GetOneThreadAllocatedMemory)
223224
(JNIEnv *env,
224225
jlong thread_id);

hotspot/src/share/vm/services/management.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "runtime/interfaceSupport.hpp"
3838
#include "runtime/javaCalls.hpp"
3939
#include "runtime/jniHandles.hpp"
40+
#include "runtime/mutexLocker.hpp"
4041
#include "runtime/os.hpp"
4142
#include "runtime/serviceThread.hpp"
4243
#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

@@ -445,8 +444,6 @@ static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
445444
}
446445
}
447446

448-
#if INCLUDE_MANAGEMENT
449-
450447
static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
451448
// check if the element of infoArray is of type ThreadInfo class
452449
Klass* threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK);
@@ -2230,7 +2227,39 @@ jlong Management::ticks_to_ms(jlong ticks) {
22302227
return (jlong)(((double)ticks / (double)os::elapsed_frequency())
22312228
* (double)1000.0);
22322229
}
2233-
#endif // INCLUDE_MANAGEMENT
2230+
2231+
// Gets the amount of memory allocated on the Java heap since JVM launch.
2232+
JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env))
2233+
// We keep a high water mark to ensure monotonicity
2234+
static jlong high_water_result = 0;
2235+
static jlong prev_result = 0;
2236+
2237+
jlong result;
2238+
if (Threads_lock->try_lock()) {
2239+
result = ThreadService::exited_allocated_bytes();
2240+
for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
2241+
jlong size = thread->cooked_allocated_bytes();
2242+
result += size;
2243+
}
2244+
Threads_lock->unlock();
2245+
} else {
2246+
// Return the previous result if Threads_lock is locked
2247+
result = prev_result;
2248+
}
2249+
2250+
{
2251+
MutexLockerEx ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag);
2252+
if (result < high_water_result) {
2253+
// Result wrapped to a negative value, in which case it's
2254+
// pegged at the last positive value.
2255+
result = high_water_result;
2256+
} else {
2257+
high_water_result = result;
2258+
}
2259+
prev_result = result;
2260+
}
2261+
return result;
2262+
JVM_END
22342263

22352264
// Gets the amount of memory allocated on the Java heap for a single thread.
22362265
// Returns -1 if the thread does not exist or has terminated.
@@ -2368,11 +2397,8 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
23682397
}
23692398
JVM_END
23702399

2371-
2372-
2373-
#if INCLUDE_MANAGEMENT
23742400
const struct jmmInterface_1_ jmm_interface = {
2375-
NULL,
2401+
jmm_GetTotalThreadAllocatedMemory,
23762402
jmm_GetOneThreadAllocatedMemory,
23772403
jmm_GetVersion,
23782404
jmm_GetOptionalSupport,

hotspot/src/share/vm/services/threadService.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ PerfVariable* ThreadService::_daemon_threads_count = NULL;
5858
volatile int ThreadService::_exiting_threads_count = 0;
5959
volatile int ThreadService::_exiting_daemon_threads_count = 0;
6060

61+
volatile jlong ThreadService::_exited_allocated_bytes = 0;
62+
6163
ThreadDumpResult* ThreadService::_threaddump_list = NULL;
6264

6365
static const int INITIAL_ARRAY_SIZE = 10;
@@ -119,6 +121,9 @@ void ThreadService::add_thread(JavaThread* thread, bool daemon) {
119121
}
120122

121123
void ThreadService::remove_thread(JavaThread* thread, bool daemon) {
124+
// Include hidden thread allcations in exited_allocated_bytes
125+
ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes());
126+
122127
Atomic::dec((jint*) &_exiting_threads_count);
123128

124129
if (thread->is_hidden_from_external_view() ||

hotspot/src/share/vm/services/threadService.hpp

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

61+
static volatile jlong _exited_allocated_bytes;
62+
6163
// These 2 counters are atomically incremented once the thread is exiting.
6264
// They will be atomically decremented when ThreadService::remove_thread is called.
6365
static volatile int _exiting_threads_count;
@@ -95,6 +97,14 @@ class ThreadService : public AllStatic {
9597
static int exiting_threads_count() { return _exiting_threads_count; }
9698
static int exiting_daemon_threads_count() { return _exiting_daemon_threads_count; }
9799

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

jdk/make/mapfiles/libmanagement/mapfile-vers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ SUNWprivate_1.1 {
8989
Java_sun_management_ThreadImpl_getThreadUserCpuTime1;
9090
Java_sun_management_ThreadImpl_getThreadAllocatedMemory0;
9191
Java_sun_management_ThreadImpl_getThreadAllocatedMemory1;
92+
Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory;
9293
Java_sun_management_ThreadImpl_resetContentionTimes0;
9394
Java_sun_management_ThreadImpl_resetPeakThreadCount0;
9495
Java_sun_management_ThreadImpl_setThreadContentionMonitoringEnabled0;

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

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

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

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,13 @@ public void setThreadCpuTimeEnabled(boolean enable) {
331331
}
332332
}
333333

334+
public long getTotalThreadAllocatedBytes() {
335+
if (isThreadAllocatedMemoryEnabled()) {
336+
return getTotalThreadAllocatedMemory();
337+
}
338+
return -1;
339+
}
340+
334341
public long getCurrentThreadAllocatedBytes() {
335342
if (isThreadAllocatedMemoryEnabled()) {
336343
return getThreadAllocatedMemory0(0);
@@ -507,6 +514,7 @@ private static native void getThreadInfo1(long[] ids,
507514
private static native void getThreadUserCpuTime1(long[] ids, long[] result);
508515
private static native long getThreadAllocatedMemory0(long id);
509516
private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
517+
private static native long getTotalThreadAllocatedMemory();
510518
private static native void setThreadCpuTimeEnabled0(boolean enable);
511519
private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
512520
private static native void setThreadContentionMonitoringEnabled0(boolean enable);

jdk/src/share/javavm/export/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, 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
@@ -218,7 +218,8 @@ typedef struct {
218218
} dcmdArgInfo;
219219

220220
typedef struct jmmInterface_1_ {
221-
void* reserved1;
221+
jlong (JNICALL *GetTotalThreadAllocatedMemory)
222+
(JNIEnv *env);
222223
jlong (JNICALL *GetOneThreadAllocatedMemory)
223224
(JNIEnv *env,
224225
jlong thread_id);

0 commit comments

Comments
 (0)