1 /*
2 * Copyright Amazon.com Inc. 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_SHENANDOAH_SHENANDOAHAGECENSUS_HPP
26 #define SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP
27
28 #include "gc/shared/ageTable.hpp"
29
30 #ifndef PRODUCT
31 // Enable noise instrumentation
32 #define SHENANDOAH_CENSUS_NOISE 1
33 #endif // PRODUCT
34
35 #ifdef SHENANDOAH_CENSUS_NOISE
36
37 #define CENSUS_NOISE(x) x
38 #define NO_CENSUS_NOISE(x)
39
40 class LogStream;
41
42 struct ShenandoahNoiseStats {
43 size_t skipped; // Volume of objects skipped
44 size_t aged; // Volume of objects from aged regions
45 size_t clamped; // Volume of objects whose ages were clamped
46 size_t young; // Volume of (rejuvenated) objects of retrograde age
47
48 ShenandoahNoiseStats() {
49 clear();
50 }
51
52 void clear() {
53 skipped = 0;
54 aged = 0;
55 clamped = 0;
56 young = 0;
57 }
58
59 #ifndef PRODUCT
60 bool is_clear() {
61 return (skipped + aged + clamped + young) == 0;
62 }
63 #endif // !PRODUCT
64
65 void merge(ShenandoahNoiseStats& other) {
66 skipped += other.skipped;
67 aged += other.aged;
68 clamped += other.clamped;
69 young += other.young;
70 }
71
72 void print(LogStream& ls, size_t total);
73 };
74 #else // SHENANDOAH_CENSUS_NOISE
75 #define CENSUS_NOISE(x)
76 #define NO_CENSUS_NOISE(x) x
77 #endif // SHENANDOAH_CENSUS_NOISE
78
79 // A class for tracking a sequence of cohort population vectors (or,
80 // interchangeably, age tables) for up to C=MAX_COHORTS age cohorts, where a cohort
81 // represents the set of objects allocated during a specific inter-GC epoch.
82 // Epochs are demarcated by GC cycles, with those surviving a cycle aging by
83 // an epoch. The census tracks the historical variation of cohort demographics
84 // across N=MAX_SNAPSHOTS recent epochs. Since there are at most C age cohorts in
85 // the population, we need only track at most N=C epochal snapshots to track a
86 // maximal longitudinal demographics of every object's longitudinal cohort in
87 // the young generation. The _global_age_table is thus, currently, a C x N (row-major)
88 // matrix, with C=16, and, for now N=C=16, currently.
89 // In theory, we might decide to track even longer (N=MAX_SNAPSHOTS) demographic
90 // histories, but that isn't the case today. In particular, the current tenuring
91 // threshold algorithm uses only 2 most recent snapshots, with the remaining
92 // MAX_SNAPSHOTS-2=14 reserved for research purposes.
93 //
94 // In addition, this class also maintains per worker population vectors into which
95 // census for the current minor GC is accumulated (during marking or, optionally, during
96 // evacuation). These are cleared after each marking (respectively, evacuation) cycle,
97 // once the per-worker data is consolidated into the appropriate population vector
98 // per minor collection. The _local_age_table is thus C x N, for N GC workers.
99 class ShenandoahAgeCensus: public CHeapObj<mtGC> {
100 AgeTable** _global_age_table; // Global age table used for adapting tenuring threshold, one per snapshot
101 AgeTable** _local_age_table; // Local scratch age tables to track object ages, one per worker
102
103 #ifdef SHENANDOAH_CENSUS_NOISE
104 ShenandoahNoiseStats* _global_noise; // Noise stats, one per snapshot
105 ShenandoahNoiseStats* _local_noise; // Local scratch table for noise stats, one per worker
106
107 size_t _skipped; // net size of objects encountered, but skipped during census,
108 // because their age was indeterminate
109 #endif // SHENANDOAH_CENSUS_NOISE
110
111 #ifndef PRODUCT
112 size_t _counted; // net size of objects counted in census
113 size_t _total; // net size of objects encountered (counted or skipped) in census
114 #endif
115
116 uint _epoch; // Current epoch (modulo max age)
117 uint* _tenuring_threshold; // An array of the last N tenuring threshold values we
118 // computed.
119
120 uint _max_workers; // Maximum number of workers for parallel tasks
121
122 // Mortality rate of a cohort, given its population in
123 // previous and current epochs
124 double mortality_rate(size_t prev_pop, size_t cur_pop);
125
126 // Update to a new epoch, creating a slot for new census.
127 void prepare_for_census_update();
128
129 // Update the tenuring threshold, calling
130 // compute_tenuring_threshold() to calculate the new
131 // value
132 void update_tenuring_threshold();
133
134 // Use _global_age_table and the current _epoch to compute a new tenuring
135 // threshold, which will be remembered until the next invocation of
136 // compute_tenuring_threshold.
137 uint compute_tenuring_threshold();
138
139 // Return the tenuring threshold computed for the previous epoch
140 uint previous_tenuring_threshold() const {
141 assert(_epoch < MAX_SNAPSHOTS, "Error");
142 uint prev = _epoch - 1;
143 if (prev >= MAX_SNAPSHOTS) {
144 // _epoch is 0
145 assert(_epoch == 0, "Error");
146 prev = MAX_SNAPSHOTS - 1;
147 }
148 return _tenuring_threshold[prev];
149 }
150
151 #ifndef PRODUCT
152 // Return the sum of size of objects of all ages recorded in the
153 // census at snapshot indexed by snap.
154 size_t get_all_ages(uint snap);
155
156 // Return the size of all objects that were encountered, but skipped,
157 // during the census, because their age was indeterminate.
158 size_t get_skipped(uint snap);
159
160 // Update the total size of objects counted or skipped at the census for
161 // the most recent epoch.
162 void update_total();
163 #endif // !PRODUCT
164
165 public:
166 enum {
167 MAX_COHORTS = AgeTable::table_size, // = markWord::max_age + 1
168 MAX_SNAPSHOTS = MAX_COHORTS // May change in the future
169 };
170
171 ShenandoahAgeCensus();
172 ShenandoahAgeCensus(uint max_workers);
173 ~ShenandoahAgeCensus();
174
175 // Return the local age table (population vector) for worker_id.
176 // Only used in the case of (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac)
177 AgeTable* get_local_age_table(uint worker_id) const {
178 return _local_age_table[worker_id];
179 }
180
181 // Return the most recently computed tenuring threshold.
182 // Visible for testing. Use is_tenurable for consistent tenuring comparisons.
183 uint tenuring_threshold() const { return _tenuring_threshold[_epoch]; }
184
185 // Return true if this age is at or above the tenuring threshold.
186 bool is_tenurable(uint age) const {
187 return age >= tenuring_threshold();
188 }
189
190 // Update the local age table for worker_id by size for
191 // given obj_age, region_age, and region_youth
192 CENSUS_NOISE(void add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id);)
193 NO_CENSUS_NOISE(void add(uint obj_age, uint region_age, size_t size, uint worker_id);)
194
195 #ifdef SHENANDOAH_CENSUS_NOISE
196 // Update the local skip table for worker_id by size
197 void add_skipped(size_t size, uint worker_id);
198 // Update the local aged region volume table for worker_id by size
199 void add_aged(size_t size, uint worker_id);
200 // Update the local clamped object volume table for worker_id by size
201 void add_clamped(size_t size, uint worker_id);
202 // Update the local (rejuvenated) object volume (retrograde age) for worker_id by size
203 void add_young(size_t size, uint worker_id);
204 #endif // SHENANDOAH_CENSUS_NOISE
205
206 // Update the census data, and compute the new tenuring threshold.
207 // This method should be called at the end of each marking (or optionally
208 // evacuation) cycle to update the tenuring threshold to be used in
209 // the next cycle.
210 // age0_pop is the population of Cohort 0 that may have been missed in
211 // the regular census during the marking cycle, corresponding to objects
212 // allocated when the concurrent marking was in progress.
213 // Optional parameters, pv1 and pv2 are population vectors that together
214 // provide object census data (only) for the case when
215 // ShenandoahGenerationalCensusAtEvac. In this case, the age0_pop
216 // is 0, because the evacuated objects have all had their ages incremented.
217 void update_census(size_t age0_pop, AgeTable* pv1 = nullptr, AgeTable* pv2 = nullptr);
218
219 // Reset the epoch, clearing accumulated census history
220 // Note: this isn't currently used, but reserved for planned
221 // future usage.
222 void reset_global();
223
224 // Reset any (potentially partial) census information in worker-local age tables
225 void reset_local();
226
227 #ifndef PRODUCT
228 // Check whether census information is clear
229 bool is_clear_global();
230 bool is_clear_local();
231
232 // Return the net size of objects encountered (counted or skipped) in census
233 // at most recent epoch.
234 size_t get_total() { return _total; }
235 #endif // !PRODUCT
236
237 // Print the age census information
238 void print();
239 };
240
241 #endif // SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP