Skip to content

Commit 56ebb8c

Browse files
JonasNorlinderfiskJonas Norlinder
authored andcommitted
8359110: Log accumulated GC and process CPU time upon VM exit
Co-authored-by: Erik Österlund <[email protected]> Co-authored-by: Jonas Norlinder <[email protected]> Reviewed-by: tschatzl, ayang
1 parent 5cf349c commit 56ebb8c

15 files changed

+245
-14
lines changed

src/hotspot/os/posix/os_posix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
#include <sys/resource.h>
7878
#include <sys/socket.h>
7979
#include <spawn.h>
80+
#include <sys/resource.h>
8081
#include <sys/time.h>
8182
#include <sys/times.h>
8283
#include <sys/types.h>
@@ -1599,6 +1600,17 @@ jlong os::elapsed_frequency() {
15991600
return NANOSECS_PER_SEC; // nanosecond resolution
16001601
}
16011602

1603+
double os::elapsed_process_cpu_time() {
1604+
struct rusage usage;
1605+
int retval = getrusage(RUSAGE_SELF, &usage);
1606+
if (retval == 0) {
1607+
return usage.ru_utime.tv_sec + usage.ru_stime.tv_sec +
1608+
(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000.0 * 1000.0);
1609+
} else {
1610+
return -1;
1611+
}
1612+
}
1613+
16021614
// Return the real, user, and system times in seconds from an
16031615
// arbitrary fixed point in the past.
16041616
bool os::getTimesSecs(double* process_real_time,

src/hotspot/os/windows/os_windows.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,38 @@ FILETIME java_to_windows_time(jlong l) {
11941194
return result;
11951195
}
11961196

1197+
double os::elapsed_process_cpu_time() {
1198+
FILETIME create;
1199+
FILETIME exit;
1200+
FILETIME kernel;
1201+
FILETIME user;
1202+
1203+
if (GetProcessTimes(GetCurrentProcess(), &create, &exit, &kernel, &user) == 0) {
1204+
return -1;
1205+
}
1206+
1207+
SYSTEMTIME user_total;
1208+
if (FileTimeToSystemTime(&user, &user_total) == 0) {
1209+
return -1;
1210+
}
1211+
1212+
SYSTEMTIME kernel_total;
1213+
if (FileTimeToSystemTime(&kernel, &kernel_total) == 0) {
1214+
return -1;
1215+
}
1216+
1217+
double user_seconds =
1218+
double(user_total.wHour) * 3600.0 + double(user_total.wMinute) * 60.0 +
1219+
double(user_total.wSecond) + double(user_total.wMilliseconds) / 1000.0;
1220+
1221+
double kernel_seconds = double(kernel_total.wHour) * 3600.0 +
1222+
double(kernel_total.wMinute) * 60.0 +
1223+
double(kernel_total.wSecond) +
1224+
double(kernel_total.wMilliseconds) / 1000.0;
1225+
1226+
return user_seconds + kernel_seconds;
1227+
}
1228+
11971229
jlong os::javaTimeMillis() {
11981230
FILETIME wt;
11991231
GetSystemTimeAsFileTime(&wt);

src/hotspot/share/gc/epsilon/epsilonHeap.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class EpsilonHeap : public CollectedHeap {
6767
jint initialize() override;
6868
void initialize_serviceability() override;
6969

70+
void stop() override {};
71+
7072
GrowableArray<GCMemoryManager*> memory_managers() override;
7173
GrowableArray<MemoryPool*> memory_pools() override;
7274

src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ class ParallelScavengeHeap : public CollectedHeap {
155155
void post_initialize() override;
156156
void update_counters();
157157

158+
void stop() override {};
159+
158160
size_t capacity() const override;
159161
size_t used() const override;
160162

src/hotspot/share/gc/serial/serialHeap.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ class SerialHeap : public CollectedHeap {
125125
// Does operations required after initialization has been done.
126126
void post_initialize() override;
127127

128+
void stop() override {};
129+
128130
bool is_in_reserved(const void* addr) const { return _reserved.contains(addr); }
129131

130132
// Performance Counter support

src/hotspot/share/gc/shared/collectedHeap.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "gc/shared/gcWhen.hpp"
3939
#include "gc/shared/memAllocator.hpp"
4040
#include "gc/shared/stringdedup/stringDedup.hpp"
41+
#include "gc/shared/stringdedup/stringDedupProcessor.hpp"
4142
#include "gc/shared/tlab_globals.hpp"
4243
#include "logging/log.hpp"
4344
#include "logging/logStream.hpp"
@@ -200,6 +201,34 @@ void CollectedHeap::print_relative_to_gc(GCWhen::Type when) const {
200201
}
201202
}
202203

204+
class CPUTimeThreadClosure : public ThreadClosure {
205+
private:
206+
jlong _cpu_time = 0;
207+
208+
public:
209+
virtual void do_thread(Thread* thread) {
210+
jlong cpu_time = os::thread_cpu_time(thread);
211+
if (cpu_time != -1) {
212+
_cpu_time += cpu_time;
213+
}
214+
}
215+
jlong cpu_time() { return _cpu_time; };
216+
};
217+
218+
double CollectedHeap::elapsed_gc_cpu_time() const {
219+
double string_dedup_cpu_time = UseStringDeduplication ?
220+
os::thread_cpu_time((Thread*)StringDedup::_processor->_thread) : 0;
221+
222+
if (string_dedup_cpu_time == -1) {
223+
string_dedup_cpu_time = 0;
224+
}
225+
226+
CPUTimeThreadClosure cl;
227+
gc_threads_do(&cl);
228+
229+
return (double)(cl.cpu_time() + _vmthread_cpu_time + string_dedup_cpu_time) / NANOSECS_PER_SEC;
230+
}
231+
203232
void CollectedHeap::print_before_gc() const {
204233
print_relative_to_gc(GCWhen::BeforeGC);
205234
}
@@ -281,6 +310,7 @@ CollectedHeap::CollectedHeap() :
281310
_last_whole_heap_examined_time_ns(os::javaTimeNanos()),
282311
_total_collections(0),
283312
_total_full_collections(0),
313+
_vmthread_cpu_time(0),
284314
_gc_cause(GCCause::_no_gc),
285315
_gc_lastcause(GCCause::_no_gc)
286316
{
@@ -603,6 +633,40 @@ void CollectedHeap::post_initialize() {
603633
initialize_serviceability();
604634
}
605635

636+
void CollectedHeap::log_gc_cpu_time() const {
637+
LogTarget(Info, gc, cpu) out;
638+
if (os::is_thread_cpu_time_supported() && out.is_enabled()) {
639+
double process_cpu_time = os::elapsed_process_cpu_time();
640+
double gc_cpu_time = elapsed_gc_cpu_time();
641+
642+
if (process_cpu_time == -1 || gc_cpu_time == -1) {
643+
log_warning(gc, cpu)("Could not sample CPU time");
644+
return;
645+
}
646+
647+
double usage;
648+
if (gc_cpu_time > process_cpu_time ||
649+
process_cpu_time == 0 || gc_cpu_time == 0) {
650+
// This can happen e.g. for short running processes with
651+
// low CPU utilization
652+
usage = 0;
653+
} else {
654+
usage = 100 * gc_cpu_time / process_cpu_time;
655+
}
656+
out.print("GC CPU usage: %.2f%% (Process: %.4fs GC: %.4fs)", usage, process_cpu_time, gc_cpu_time);
657+
}
658+
}
659+
660+
void CollectedHeap::before_exit() {
661+
print_tracing_info();
662+
663+
// Log GC CPU usage.
664+
log_gc_cpu_time();
665+
666+
// Stop any on-going concurrent work and prepare for exit.
667+
stop();
668+
}
669+
606670
#ifndef PRODUCT
607671

608672
bool CollectedHeap::promotion_should_fail(volatile size_t* count) {

src/hotspot/share/gc/shared/collectedHeap.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class CollectedHeap : public CHeapObj<mtGC> {
132132
NOT_PRODUCT(volatile size_t _promotion_failure_alot_count;)
133133
NOT_PRODUCT(volatile size_t _promotion_failure_alot_gc_number;)
134134

135+
jlong _vmthread_cpu_time;
136+
135137
// Reason for current garbage collection. Should be set to
136138
// a value reflecting no collection between collections.
137139
GCCause::Cause _gc_cause;
@@ -206,6 +208,9 @@ class CollectedHeap : public CHeapObj<mtGC> {
206208
return static_cast<T*>(heap);
207209
}
208210

211+
// Stop any onging concurrent work and prepare for exit.
212+
virtual void stop() = 0;
213+
209214
public:
210215

211216
static inline size_t filler_array_max_size() {
@@ -239,13 +244,14 @@ class CollectedHeap : public CHeapObj<mtGC> {
239244
// This is the correct place to place such initialization methods.
240245
virtual void post_initialize();
241246

242-
// Stop any onging concurrent work and prepare for exit.
243-
virtual void stop() {}
247+
void before_exit();
244248

245249
// Stop and resume concurrent GC threads interfering with safepoint operations
246250
virtual void safepoint_synchronize_begin() {}
247251
virtual void safepoint_synchronize_end() {}
248252

253+
void add_vmthread_cpu_time(jlong time);
254+
249255
void initialize_reserved_region(const ReservedHeapSpace& rs);
250256

251257
virtual size_t capacity() const = 0;
@@ -419,6 +425,8 @@ class CollectedHeap : public CHeapObj<mtGC> {
419425

420426
void print_relative_to_gc(GCWhen::Type when) const;
421427

428+
void log_gc_cpu_time() const;
429+
422430
public:
423431
void pre_full_gc_dump(GCTimer* timer);
424432
void post_full_gc_dump(GCTimer* timer);
@@ -455,6 +463,8 @@ class CollectedHeap : public CHeapObj<mtGC> {
455463
// Default implementation does nothing.
456464
virtual void print_tracing_info() const = 0;
457465

466+
double elapsed_gc_cpu_time() const;
467+
458468
void print_before_gc() const;
459469
void print_after_gc() const;
460470

src/hotspot/share/gc/shared/collectedHeap.inline.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,8 @@ inline oop CollectedHeap::class_allocate(Klass* klass, size_t size, TRAPS) {
4646
return allocator.allocate();
4747
}
4848

49+
inline void CollectedHeap::add_vmthread_cpu_time(jlong time) {
50+
_vmthread_cpu_time += time;
51+
}
52+
4953
#endif // SHARE_GC_SHARED_COLLECTEDHEAP_INLINE_HPP

src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
#include "oops/oopsHierarchy.hpp"
106106
#include "utilities/globalDefinitions.hpp"
107107

108+
class CollectedHeap;
108109
class Klass;
109110
class StringDedupThread;
110111
class ThreadClosure;
@@ -115,6 +116,7 @@ class ThreadClosure;
115116
// feature. Other functions in the StringDedup class are called where
116117
// needed, without requiring GC-specific code.
117118
class StringDedup : public AllStatic {
119+
friend class CollectedHeap;
118120
friend class StringDedupThread;
119121

120122
class Config;

src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "memory/allocation.hpp"
3030
#include "utilities/macros.hpp"
3131

32+
class CollectedHeap;
3233
class JavaThread;
3334
class OopStorage;
3435

@@ -42,6 +43,8 @@ class OopStorage;
4243
// incremental operations for resizing and for removing dead entries, so
4344
// safepoint checks can be performed between steps in those operations.
4445
class StringDedup::Processor : public CHeapObj<mtGC> {
46+
friend class CollectedHeap;
47+
4548
Processor();
4649
~Processor() = default;
4750

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
#ifndef SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_HPP
26+
#define SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_HPP
27+
28+
#include "memory/allocation.hpp"
29+
30+
class VMThread;
31+
32+
class VMThreadCPUTimeScope : public StackObj {
33+
private:
34+
jlong _start;
35+
bool _enabled;
36+
bool _is_gc_operation;
37+
VMThread* _thread;
38+
39+
public:
40+
VMThreadCPUTimeScope(VMThread* thread, bool is_gc_operation);
41+
~VMThreadCPUTimeScope();
42+
};
43+
44+
#endif // SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_HPP
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
#include "gc/shared/vmThreadCpuTimeScope.hpp"
26+
27+
#include "gc/shared/collectedHeap.inline.hpp"
28+
#include "memory/universe.hpp"
29+
#include "runtime/cpuTimeCounters.hpp"
30+
#include "runtime/os.hpp"
31+
#include "runtime/vmThread.hpp"
32+
33+
inline VMThreadCPUTimeScope::VMThreadCPUTimeScope(VMThread* thread, bool is_gc_operation)
34+
: _start(0),
35+
_enabled(os::is_thread_cpu_time_supported()),
36+
_is_gc_operation(is_gc_operation),
37+
_thread(thread) {
38+
if (_is_gc_operation && _enabled) {
39+
_start = os::thread_cpu_time(_thread);
40+
}
41+
}
42+
43+
inline VMThreadCPUTimeScope::~VMThreadCPUTimeScope() {
44+
if (!_enabled) {
45+
return;
46+
}
47+
48+
jlong end = (_is_gc_operation || UsePerfData) ? os::thread_cpu_time(_thread) : 0;
49+
50+
if (_is_gc_operation) {
51+
jlong duration = end > _start ? end - _start : 0;
52+
Universe::heap()->add_vmthread_cpu_time(duration);
53+
}
54+
55+
if (UsePerfData) {
56+
CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType::vm, end);
57+
}
58+
}

src/hotspot/share/runtime/java.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,8 @@ void before_exit(JavaThread* thread, bool halt) {
471471

472472
NativeHeapTrimmer::cleanup();
473473

474-
Universe::heap()->print_tracing_info();
475-
// Stop concurrent GC threads
476-
Universe::heap()->stop();
474+
// Run before exit and then stop concurrent GC threads
475+
Universe::heap()->before_exit();
477476

478477
// Print GC/heap related information.
479478
Log(gc, exit) log;

0 commit comments

Comments
 (0)