1 /*
2 * Copyright (c) 2001, 2020, 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 "precompiled.hpp"
26 #include "gc/g1/g1MMUTracker.hpp"
27 #include "gc/g1/g1Trace.hpp"
28 #include "logging/log.hpp"
29 #include "runtime/mutexLocker.hpp"
30 #include "utilities/ostream.hpp"
31
32 // can't rely on comparing doubles with tolerating a small margin for error
33 #define SMALL_MARGIN 0.0000001
34 #define is_double_leq_0(_value) ( (_value) < SMALL_MARGIN )
35 #define is_double_leq(_val1, _val2) is_double_leq_0((_val1) - (_val2))
36 #define is_double_geq(_val1, _val2) is_double_leq_0((_val2) - (_val1))
37
38 /***** ALL TIMES ARE IN SECS!!!!!!! *****/
39
G1MMUTracker(double time_slice,double max_gc_time)40 G1MMUTracker::G1MMUTracker(double time_slice, double max_gc_time) :
41 _time_slice(time_slice),
42 _max_gc_time(max_gc_time),
43 _head_index(0),
44 _tail_index(trim_index(_head_index+1)),
45 _no_entries(0) { }
46
remove_expired_entries(double current_time)47 void G1MMUTracker::remove_expired_entries(double current_time) {
48 double limit = current_time - _time_slice;
49 while (_no_entries > 0) {
50 if (is_double_geq(limit, _array[_tail_index].end_time())) {
51 _tail_index = trim_index(_tail_index + 1);
52 --_no_entries;
53 } else
54 return;
55 }
56 guarantee(_no_entries == 0, "should have no entries in the array");
57 }
58
calculate_gc_time(double current_time)59 double G1MMUTracker::calculate_gc_time(double current_time) {
60 double gc_time = 0.0;
61 double limit = current_time - _time_slice;
62 for (int i = 0; i < _no_entries; ++i) {
63 int index = trim_index(_tail_index + i);
64 G1MMUTrackerElem *elem = &_array[index];
65 if (elem->end_time() > limit) {
66 if (elem->start_time() > limit)
67 gc_time += elem->duration();
68 else
69 gc_time += elem->end_time() - limit;
70 }
71 }
72 return gc_time;
73 }
74
add_pause(double start,double end)75 void G1MMUTracker::add_pause(double start, double end) {
76 remove_expired_entries(end);
77 if (_no_entries == QueueLength) {
78 // OK, we've filled up the queue. There are a few ways
79 // of dealing with this "gracefully"
80 // increase the array size (:-)
81 // remove the oldest entry (this might allow more GC time for
82 // the time slice than what's allowed) - this is what we
83 // currently do
84 // consolidate the two entries with the minimum gap between them
85 // (this might allow less GC time than what's allowed)
86
87 // In the case where ScavengeALot is true, such overflow is not
88 // uncommon; in such cases, we can, without much loss of precision
89 // or performance (we are GC'ing most of the time anyway!),
90 // simply overwrite the oldest entry in the tracker.
91
92 _head_index = trim_index(_head_index + 1);
93 assert(_head_index == _tail_index, "Because we have a full circular buffer");
94 _tail_index = trim_index(_tail_index + 1);
95 } else {
96 _head_index = trim_index(_head_index + 1);
97 ++_no_entries;
98 }
99 _array[_head_index] = G1MMUTrackerElem(start, end);
100
101 // Current entry needs to be added before calculating the value
102 double slice_time = calculate_gc_time(end);
103 G1MMUTracer::report_mmu(_time_slice, slice_time, _max_gc_time);
104
105 if (slice_time < _max_gc_time) {
106 log_debug(gc, mmu)("MMU: %.1lfms (%.1lfms/%.1lfms)",
107 slice_time * 1000.0, _max_gc_time * 1000.0, _time_slice * 1000);
108 } else {
109 log_info(gc, mmu)("MMU target violated: %.1lfms (%.1lfms/%.1lfms)",
110 slice_time * 1000.0, _max_gc_time * 1000.0, _time_slice * 1000);
111 }
112 }
113
when_sec(double current_time,double pause_time)114 double G1MMUTracker::when_sec(double current_time, double pause_time) {
115 // if the pause is over the maximum, just assume that it's the maximum
116 double adjusted_pause_time =
117 (pause_time > max_gc_time()) ? max_gc_time() : pause_time;
118 double earliest_end = current_time + adjusted_pause_time;
119 double limit = earliest_end - _time_slice;
120 double gc_time = calculate_gc_time(earliest_end);
121 double diff = gc_time + adjusted_pause_time - max_gc_time();
122 if (is_double_leq_0(diff))
123 return 0.0;
124
125 if (adjusted_pause_time == max_gc_time()) {
126 G1MMUTrackerElem *elem = &_array[_head_index];
127 return elem->end_time() - limit;
128 }
129
130 int index = _tail_index;
131 while ( 1 ) {
132 G1MMUTrackerElem *elem = &_array[index];
133 if (elem->end_time() > limit) {
134 if (elem->start_time() > limit)
135 diff -= elem->duration();
136 else
137 diff -= elem->end_time() - limit;
138 if (is_double_leq_0(diff))
139 return elem->end_time() + diff - limit;
140 }
141 index = trim_index(index+1);
142 guarantee(index != trim_index(_head_index + 1), "should not go past head");
143 }
144 }
145