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 #include "precompiled.hpp"
25 #include "unittest.hpp"
26 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
27 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
28 #include "gc/shenandoah/shenandoahGeneration.hpp"
29 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
30 #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp"
31 #include <cstdarg>
32
33 // These tests will all be skipped (unless Shenandoah becomes the default
34 // collector). To execute these tests, you must enable Shenandoah, which
35 // is done with:
36 //
37 // % make exploded-test TEST="gtest:ShenandoahOld*" CONF=release TEST_OPTS="JAVA_OPTIONS=-XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational"
38 //
39 // Please note that these 'unit' tests are really integration tests and rely
40 // on the JVM being initialized. These tests manipulate the state of the
41 // collector in ways that are not compatible with a normal collection run.
42 // If these tests take longer than the minimum time between gc intervals -
43 // or, more likely, if you have them paused in a debugger longer than this
44 // interval - you can expect trouble. These tests will also not run in a build
45 // with asserts enabled because they use APIs that expect to run on a safepoint.
46 #ifdef ASSERT
47 #define SKIP_IF_NOT_SHENANDOAH() \
48 tty->print_cr("skipped (debug build)" ); \
49 return;
50 #else
51 #define SKIP_IF_NOT_SHENANDOAH() \
52 if (!UseShenandoahGC) { \
53 tty->print_cr("skipped"); \
54 return; \
55 }
56 #endif
57
58 class ShenandoahResetRegions : public ShenandoahHeapRegionClosure {
59 public:
60 virtual void heap_region_do(ShenandoahHeapRegion* region) override {
61 if (!region->is_empty()) {
62 region->make_trash();
63 region->make_empty();
64 }
65 region->set_affiliation(FREE);
66 region->clear_live_data();
67 region->set_top(region->bottom());
68 }
69 };
70
71 class ShenandoahOldHeuristicTest : public ::testing::Test {
72 protected:
73 ShenandoahHeap* _heap;
74 ShenandoahOldHeuristics* _heuristics;
75 ShenandoahCollectionSet* _collection_set;
76
77 ShenandoahOldHeuristicTest()
78 : _heap(nullptr),
79 _heuristics(nullptr),
80 _collection_set(nullptr) {
81 SKIP_IF_NOT_SHENANDOAH();
82 _heap = ShenandoahHeap::heap();
83 _heuristics = _heap->old_generation()->heuristics();
84 _collection_set = _heap->collection_set();
85 _heap->lock()->lock(false);
86 ShenandoahResetRegions reset;
87 _heap->heap_region_iterate(&reset);
88 _heap->old_generation()->increase_capacity(ShenandoahHeapRegion::region_size_bytes() * 10);
89 _heap->old_generation()->set_evacuation_reserve(ShenandoahHeapRegion::region_size_bytes() * 4);
90 _heuristics->abandon_collection_candidates();
91 _collection_set->clear();
92 }
93
94 ~ShenandoahOldHeuristicTest() override {
95 SKIP_IF_NOT_SHENANDOAH();
96 _heap->lock()->unlock();
97 }
98
99 ShenandoahOldGeneration::State old_generation_state() {
100 return _heap->old_generation()->state();
101 }
102
103 size_t make_garbage(size_t region_idx, size_t garbage_bytes) {
104 ShenandoahHeapRegion* region = _heap->get_region(region_idx);
105 region->set_affiliation(OLD_GENERATION);
106 region->make_regular_allocation(OLD_GENERATION);
107 size_t live_bytes = ShenandoahHeapRegion::region_size_bytes() - garbage_bytes;
108 region->increase_live_data_alloc_words(live_bytes / HeapWordSize);
109 region->set_top(region->end());
110 return region->garbage();
111 }
112
113 size_t create_too_much_garbage_for_one_mixed_evacuation() {
114 size_t garbage_target = _heap->old_generation()->max_capacity() / 2;
115 size_t garbage_total = 0;
116 size_t region_idx = 0;
117 while (garbage_total < garbage_target && region_idx < _heap->num_regions()) {
118 garbage_total += make_garbage_above_collection_threshold(region_idx++);
119 }
120 return garbage_total;
121 }
122
123 void make_pinned(size_t region_idx) {
124 ShenandoahHeapRegion* region = _heap->get_region(region_idx);
125 region->record_pin();
126 region->make_pinned();
127 }
128
129 void make_unpinned(size_t region_idx) {
130 ShenandoahHeapRegion* region = _heap->get_region(region_idx);
131 region->record_unpin();
132 region->make_unpinned();
133 }
134
135 size_t make_garbage_below_collection_threshold(size_t region_idx) {
136 return make_garbage(region_idx, collection_threshold() - 100);
137 }
138
139 size_t make_garbage_above_collection_threshold(size_t region_idx) {
140 return make_garbage(region_idx, collection_threshold() + 100);
141 }
142
143 size_t collection_threshold() const {
144 return ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold / 100;
145 }
146
147 bool collection_set_is(size_t r1) { return _collection_set_is(1, r1); }
148 bool collection_set_is(size_t r1, size_t r2) { return _collection_set_is(2, r1, r2); }
149 bool collection_set_is(size_t r1, size_t r2, size_t r3) { return _collection_set_is(3, r1, r2, r3); }
150
151 bool _collection_set_is(size_t count, ...) {
152 va_list args;
153 va_start(args, count);
154 EXPECT_EQ(count, _collection_set->count());
155 bool result = true;
156 for (size_t i = 0; i < count; ++i) {
157 size_t index = va_arg(args, size_t);
158 if (!_collection_set->is_in(index)) {
159 result = false;
160 break;
161 }
162 }
163 va_end(args);
164 return result;
165 }
166 };
167
168 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_regions) {
169 SKIP_IF_NOT_SHENANDOAH();
170
171 _heuristics->prepare_for_old_collections();
172 EXPECT_EQ(0U, _heuristics->coalesce_and_fill_candidates_count());
173 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index());
174 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
175 }
176
177 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_region_above_threshold) {
178 SKIP_IF_NOT_SHENANDOAH();
179
180 // In this case, we have zero regions to add to the collection set,
181 // but we will have one region that must still be made parseable.
182 make_garbage_below_collection_threshold(10);
183 _heuristics->prepare_for_old_collections();
184 EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count());
185 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index());
186 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
187 }
188
189 TEST_VM_F(ShenandoahOldHeuristicTest, select_one_old_region_above_threshold) {
190 SKIP_IF_NOT_SHENANDOAH();
191
192 make_garbage_above_collection_threshold(10);
193 _heuristics->prepare_for_old_collections();
194 EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count());
195 EXPECT_EQ(1U, _heuristics->last_old_collection_candidate_index());
196 EXPECT_EQ(1U, _heuristics->unprocessed_old_collection_candidates());
197 }
198
199 TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) {
200 SKIP_IF_NOT_SHENANDOAH();
201
202 size_t garbage = make_garbage_above_collection_threshold(10);
203 _heuristics->prepare_for_old_collections();
204 _heuristics->prime_collection_set(_collection_set);
205
206 EXPECT_TRUE(collection_set_is(10UL));
207 EXPECT_EQ(garbage, _collection_set->get_old_garbage());
208 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
209 }
210
211 TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) {
212 SKIP_IF_NOT_SHENANDOAH();
213
214 size_t g1 = make_garbage_above_collection_threshold(100);
215 size_t g2 = make_garbage_above_collection_threshold(101);
216 _heuristics->prepare_for_old_collections();
217 _heuristics->prime_collection_set(_collection_set);
218
219 EXPECT_TRUE(collection_set_is(100UL, 101UL));
220 EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage());
221 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
222 }
223
224 TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) {
225 SKIP_IF_NOT_SHENANDOAH();
226
227 size_t garbage = create_too_much_garbage_for_one_mixed_evacuation();
228 _heuristics->prepare_for_old_collections();
229 _heuristics->prime_collection_set(_collection_set);
230
231 EXPECT_LT(_collection_set->get_old_garbage(), garbage);
232 EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL);
233 }
234
235 TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) {
236 SKIP_IF_NOT_SHENANDOAH();
237
238 // Create three old regions with enough garbage to be collected.
239 size_t g1 = make_garbage_above_collection_threshold(0);
240 size_t g2 = make_garbage_above_collection_threshold(1);
241 size_t g3 = make_garbage_above_collection_threshold(2);
242
243 // A region can be pinned when we chose collection set candidates.
244 make_pinned(1);
245 _heuristics->prepare_for_old_collections();
246
247 // We only exclude pinned regions when we actually add regions to the collection set.
248 ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates());
249
250 // Here the region is still pinned, so it cannot be added to the collection set.
251 _heuristics->prime_collection_set(_collection_set);
252
253 // The two unpinned regions should be added to the collection set and the pinned
254 // region should be retained at the front of the list of candidates as it would be
255 // likely to become unpinned by the next mixed collection cycle.
256 EXPECT_TRUE(collection_set_is(0UL, 2UL));
257 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3);
258 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
259
260 // Simulate another mixed collection after making region 1 unpinned. This time,
261 // the now unpinned region should be added to the collection set.
262 make_unpinned(1);
263 _collection_set->clear();
264 _heuristics->prime_collection_set(_collection_set);
265
266 EXPECT_EQ(_collection_set->get_old_garbage(), g2);
267 EXPECT_TRUE(collection_set_is(1UL));
268 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
269 }
270
271 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) {
272 SKIP_IF_NOT_SHENANDOAH();
273
274 // Create three old regions with enough garbage to be collected.
275 size_t g1 = make_garbage_above_collection_threshold(0);
276 size_t g2 = make_garbage_above_collection_threshold(1);
277 size_t g3 = make_garbage_above_collection_threshold(2);
278
279 make_pinned(0);
280 _heuristics->prepare_for_old_collections();
281 _heuristics->prime_collection_set(_collection_set);
282
283 EXPECT_TRUE(collection_set_is(1UL, 2UL));
284 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
285
286 make_unpinned(0);
287 _collection_set->clear();
288 _heuristics->prime_collection_set(_collection_set);
289
290 EXPECT_TRUE(collection_set_is(0UL));
291 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
292 }
293
294 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) {
295 SKIP_IF_NOT_SHENANDOAH();
296
297 // Create three old regions with enough garbage to be collected.
298 size_t g1 = make_garbage_above_collection_threshold(0);
299 size_t g2 = make_garbage_above_collection_threshold(1);
300 size_t g3 = make_garbage_above_collection_threshold(2);
301
302 make_pinned(2);
303 _heuristics->prepare_for_old_collections();
304 _heuristics->prime_collection_set(_collection_set);
305
306 EXPECT_TRUE(collection_set_is(0UL, 1UL));
307 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2);
308 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
309
310 make_unpinned(2);
311 _collection_set->clear();
312 _heuristics->prime_collection_set(_collection_set);
313
314 EXPECT_TRUE(collection_set_is(2UL));
315 EXPECT_EQ(_collection_set->get_old_garbage(), g3);
316 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
317 }
318
319 TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) {
320 SKIP_IF_NOT_SHENANDOAH();
321
322 // Create three old regions with enough garbage to be collected.
323 size_t g1 = make_garbage_above_collection_threshold(0);
324 size_t g2 = make_garbage_above_collection_threshold(1);
325 size_t g3 = make_garbage_above_collection_threshold(2);
326
327 make_pinned(0);
328 make_pinned(2);
329 _heuristics->prepare_for_old_collections();
330 _heuristics->prime_collection_set(_collection_set);
331
332 EXPECT_TRUE(collection_set_is(1UL));
333 EXPECT_EQ(_collection_set->get_old_garbage(), g2);
334 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 2UL);
335
336 make_unpinned(0);
337 make_unpinned(2);
338 _collection_set->clear();
339 _heuristics->prime_collection_set(_collection_set);
340
341 EXPECT_TRUE(collection_set_is(0UL, 2UL));
342 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3);
343 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
344 }
345
346 TEST_VM_F(ShenandoahOldHeuristicTest, all_candidates_are_pinned) {
347 SKIP_IF_NOT_SHENANDOAH();
348
349 size_t g1 = make_garbage_above_collection_threshold(0);
350 size_t g2 = make_garbage_above_collection_threshold(1);
351 size_t g3 = make_garbage_above_collection_threshold(2);
352
353 make_pinned(0);
354 make_pinned(1);
355 make_pinned(2);
356 _heuristics->prepare_for_old_collections();
357 _heuristics->prime_collection_set(_collection_set);
358
359 // In the case when all candidates are pinned, we want to abandon
360 // this set of mixed collection candidates so that another old collection
361 // can run. This is meant to defend against "bad" JNI code that permanently
362 // leaves an old region in the pinned state.
363 EXPECT_EQ(_collection_set->count(), 0UL);
364 EXPECT_EQ(old_generation_state(), ShenandoahOldGeneration::FILLING);
365 }
366 #undef SKIP_IF_NOT_SHENANDOAH