Skip to content

Commit 812add2

Browse files
JonasNorlinderkevinjwalls
authored andcommitted
8368527: JMX: Add an MXBeans method to query GC CPU time
Reviewed-by: phh, kevinw
1 parent 09b25cd commit 812add2

File tree

12 files changed

+304
-3
lines changed

12 files changed

+304
-3
lines changed

src/hotspot/share/include/jmm.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ enum {
5353
JMM_VERSION_2 = 0x20020000, // JDK 10
5454
JMM_VERSION_3 = 0x20030000, // JDK 14
5555
JMM_VERSION_4 = 0x20040000, // JDK 21
56-
JMM_VERSION = JMM_VERSION_4
56+
JMM_VERSION_5 = 0x20050000, // JDK 26
57+
JMM_VERSION = JMM_VERSION_5
5758
};
5859

5960
typedef struct {
@@ -81,6 +82,7 @@ typedef enum {
8182
JMM_GC_TIME_MS = 9, /* Total accumulated time spent in collection */
8283
JMM_GC_COUNT = 10, /* Total number of collections */
8384
JMM_JVM_UPTIME_MS = 11, /* The JVM uptime in milliseconds */
85+
JMM_TOTAL_GC_CPU_TIME = 12, /* Total accumulated GC CPU time */
8486

8587
JMM_INTERNAL_ATTRIBUTE_INDEX = 100,
8688
JMM_CLASS_LOADED_BYTES = 101, /* Number of bytes loaded instance classes */

src/hotspot/share/services/cpuTimeUsage.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
volatile bool CPUTimeUsage::Error::_has_error = false;
3737

3838
static inline jlong thread_cpu_time_or_zero(Thread* thread) {
39+
assert(!Universe::is_shutting_down(), "Should not query during shutdown");
3940
jlong cpu_time = os::thread_cpu_time(thread);
4041
if (cpu_time == -1) {
4142
CPUTimeUsage::Error::mark_error();

src/hotspot/share/services/management.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "runtime/threadSMR.hpp"
5555
#include "runtime/vmOperations.hpp"
5656
#include "services/classLoadingService.hpp"
57+
#include "services/cpuTimeUsage.hpp"
5758
#include "services/diagnosticCommand.hpp"
5859
#include "services/diagnosticFramework.hpp"
5960
#include "services/finalizerService.hpp"
@@ -889,6 +890,21 @@ static jint get_num_flags() {
889890
return count;
890891
}
891892

893+
static jlong get_gc_cpu_time() {
894+
if (!os::is_thread_cpu_time_supported()) {
895+
return -1;
896+
}
897+
898+
{
899+
MutexLocker hl(Heap_lock);
900+
if (Universe::heap()->is_shutting_down()) {
901+
return -1;
902+
}
903+
904+
return CPUTimeUsage::GC::total();
905+
}
906+
}
907+
892908
static jlong get_long_attribute(jmmLongAttribute att) {
893909
switch (att) {
894910
case JMM_CLASS_LOADED_COUNT:
@@ -915,6 +931,9 @@ static jlong get_long_attribute(jmmLongAttribute att) {
915931
case JMM_JVM_UPTIME_MS:
916932
return Management::ticks_to_ms(os::elapsed_counter());
917933

934+
case JMM_TOTAL_GC_CPU_TIME:
935+
return get_gc_cpu_time();
936+
918937
case JMM_COMPILE_TOTAL_TIME_MS:
919938
return Management::ticks_to_ms(CompileBroker::total_compilation_ticks());
920939

src/java.management/share/classes/java/lang/management/MemoryMXBean.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, 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
@@ -267,6 +267,43 @@ public interface MemoryMXBean extends PlatformManagedObject {
267267
*/
268268
public MemoryUsage getNonHeapMemoryUsage();
269269

270+
/**
271+
* Returns the approximate accumulated time, in nanoseconds,
272+
* spent in garbage collection (GC).
273+
*
274+
* <p> The time spent in spent in GC is the CPU time used by
275+
* all GC activity, including any overhead, which means the
276+
* result may be non-zero even if no GC has occurred.
277+
*
278+
* This method returns {@code -1} if the platform does
279+
* not support this operation or the information is not
280+
* available.
281+
*
282+
* @apiNote
283+
* May be used in conjunction with {@link jdk.management/com.sun.management.OperatingSystemMXBean#getProcessCpuTime()}
284+
* for calculating the GC's usage of CPU time as a whole.
285+
*
286+
* @implNote The specifics on what constitutes the time spent
287+
* in GC are highly implementation dependent. In the HotSpot
288+
* Virtual Machine, this time includes relevant
289+
* implementation-specific details such as driver threads,
290+
* workers, VM Operations and string deduplication (if
291+
* enabled). Driver threads may be created by a GC to
292+
* orchestrate its work. The return value can be -1 if called
293+
* when measurement is not possible, such as during shutdown.
294+
*
295+
* @implSpec The default implementation returns {@code -1}.
296+
*
297+
* @return the total accumulated CPU time for GC in
298+
* nanoseconds, or {@code -1}.
299+
*
300+
* @since 26
301+
*/
302+
@SuppressWarnings("doclint:reference")
303+
default public long getTotalGcCpuTime() {
304+
return -1;
305+
}
306+
270307
/**
271308
* Tests if verbose output for the memory system is enabled.
272309
*
@@ -302,5 +339,4 @@ public interface MemoryMXBean extends PlatformManagedObject {
302339
* @see java.lang.System#gc()
303340
*/
304341
public void gc();
305-
306342
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public void gc() {
6767
Runtime.getRuntime().gc();
6868
}
6969

70+
public long getTotalGcCpuTime() {
71+
return jvm.getTotalGcCpuTime();
72+
}
73+
7074
// Need to make a VM call to get coherent value
7175
public MemoryUsage getHeapMemoryUsage() {
7276
return getMemoryUsage0(true);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public interface VMManagement {
5555
public boolean getVerboseClass();
5656

5757
// Memory Subsystem
58+
public long getTotalGcCpuTime();
5859
public boolean getVerboseGC();
5960

6061
// Runtime Subsystem

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public int getLoadedClassCount() {
128128
public native boolean getVerboseClass();
129129

130130
// Memory Subsystem
131+
public native long getTotalGcCpuTime();
131132
public native boolean getVerboseGC();
132133

133134
// Runtime Subsystem

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ Java_sun_management_VMManagementImpl_getUnloadedClassCount
121121
return count;
122122
}
123123

124+
JNIEXPORT jlong JNICALL
125+
Java_sun_management_VMManagementImpl_getTotalGcCpuTime
126+
(JNIEnv *env, jobject dummy)
127+
{
128+
return jmm_interface->GetLongAttribute(env, NULL, JMM_TOTAL_GC_CPU_TIME);
129+
}
130+
124131
JNIEXPORT jboolean JNICALL
125132
Java_sun_management_VMManagementImpl_getVerboseGC
126133
(JNIEnv *env, jobject dummy)

test/hotspot/jtreg/vmTestbase/nsk/monitoring/share/server/ServerMemoryMXBean.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public int getObjectPendingFinalizationCount() {
5858
return getIntAttribute(OBJECT_PENDING_FINALIZATION_COUNT);
5959
}
6060

61+
public long getTotalGcCpuTime() {
62+
throw new UnsupportedOperationException("This method is not supported");
63+
}
64+
6165
public boolean isVerbose() {
6266
return getBooleanAttribute(VERBOSE);
6367
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test id=Epsilon
26+
* @requires vm.gc.Epsilon
27+
* @bug 8368527
28+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
29+
* @library /test/lib
30+
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC StressGetTotalGcCpuTimeDuringShutdown
31+
*/
32+
33+
/*
34+
* @test id=Serial
35+
* @requires vm.gc.Serial
36+
* @bug 8368527
37+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
38+
* @library /test/lib
39+
* @run main/othervm -XX:+UseSerialGC StressGetTotalGcCpuTimeDuringShutdown
40+
*/
41+
42+
/*
43+
* @test id=Parallel
44+
* @requires vm.gc.Parallel
45+
* @bug 8368527
46+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
47+
* @library /test/lib
48+
* @run main/othervm -XX:+UseParallelGC StressGetTotalGcCpuTimeDuringShutdown
49+
*/
50+
51+
/*
52+
* @test id=G1
53+
* @requires vm.gc.G1
54+
* @bug 8368527
55+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
56+
* @library /test/lib
57+
* @run main/othervm -XX:+UseG1GC StressGetTotalGcCpuTimeDuringShutdown
58+
*/
59+
60+
/*
61+
* @test id=ZGC
62+
* @requires vm.gc.Z
63+
* @bug 8368527
64+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
65+
* @library /test/lib
66+
* @run main/othervm -XX:+UseZGC StressGetTotalGcCpuTimeDuringShutdown
67+
*/
68+
69+
/*
70+
* @test id=Shenandoah
71+
* @requires vm.gc.Shenandoah
72+
* @bug 8368527
73+
* @summary Stress MemoryMXBean.getTotalGcCpuTime during shutdown
74+
* @library /test/lib
75+
* @run main/othervm -XX:+UseShenandoahGC StressGetTotalGcCpuTimeDuringShutdown
76+
*/
77+
78+
import java.lang.management.ManagementFactory;
79+
import java.lang.management.MemoryMXBean;
80+
import java.lang.management.ThreadMXBean;
81+
82+
public class StressGetTotalGcCpuTimeDuringShutdown {
83+
static final ThreadMXBean mxThreadBean = ManagementFactory.getThreadMXBean();
84+
static final MemoryMXBean mxMemoryBean = ManagementFactory.getMemoryMXBean();
85+
86+
public static void main(String[] args) throws Exception {
87+
try {
88+
if (!mxThreadBean.isThreadCpuTimeEnabled()) {
89+
return;
90+
}
91+
} catch (UnsupportedOperationException e) {
92+
if (mxMemoryBean.getTotalGcCpuTime() != -1) {
93+
throw new RuntimeException("GC CPU time should be -1");
94+
}
95+
return;
96+
}
97+
98+
final int numberOfThreads = Runtime.getRuntime().availableProcessors() * 8;
99+
for (int i = 0; i < numberOfThreads; i++) {
100+
Thread t = new Thread(() -> {
101+
while (true) {
102+
long gcCpuTimeFromThread = mxMemoryBean.getTotalGcCpuTime();
103+
if (gcCpuTimeFromThread < -1) {
104+
throw new RuntimeException("GC CPU time should never be less than -1 but was " + gcCpuTimeFromThread);
105+
}
106+
}
107+
});
108+
t.setDaemon(true);
109+
t.start();
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)