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