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_heuristics(); 84 _collection_set = _heap->collection_set(); 85 ShenandoahHeapLocker locker(_heap->lock()); 86 ShenandoahResetRegions reset; 87 _heap->heap_region_iterate(&reset); 88 _heap->set_old_evac_reserve(_heap->old_generation()->soft_max_capacity() / 4); 89 _heuristics->abandon_collection_candidates(); 90 _collection_set->clear(); 91 } 92 93 ShenandoahOldGeneration::State old_generation_state() { 94 return _heap->old_generation()->state(); 95 } 96 97 size_t make_garbage(size_t region_idx, size_t garbage_bytes) { 98 ShenandoahHeapLocker locker(_heap->lock()); 99 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 100 region->set_affiliation(OLD_GENERATION); 101 region->make_regular_allocation(OLD_GENERATION); 102 size_t live_bytes = ShenandoahHeapRegion::region_size_bytes() - garbage_bytes; 103 region->increase_live_data_alloc_words(live_bytes / HeapWordSize); 104 region->set_top(region->end()); 105 return region->garbage(); 106 } 107 108 size_t create_too_much_garbage_for_one_mixed_evacuation() { 109 size_t garbage_target = _heap->old_generation()->soft_max_capacity() / 2; 110 size_t garbage_total = 0; 111 size_t region_idx = 0; 112 while (garbage_total < garbage_target && region_idx < _heap->num_regions()) { 113 garbage_total += make_garbage_above_collection_threshold(region_idx++); 114 } 115 return garbage_total; 116 } 117 118 void make_pinned(size_t region_idx) { 119 ShenandoahHeapLocker locker(_heap->lock()); 120 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 121 region->record_pin(); 122 region->make_pinned(); 123 } 124 125 void make_unpinned(size_t region_idx) { 126 ShenandoahHeapLocker locker(_heap->lock()); 127 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 128 region->record_unpin(); 129 region->make_unpinned(); 130 } 131 132 size_t make_garbage_below_collection_threshold(size_t region_idx) { 133 return make_garbage(region_idx, collection_threshold() - 100); 134 } 135 136 size_t make_garbage_above_collection_threshold(size_t region_idx) { 137 return make_garbage(region_idx, collection_threshold() + 100); 138 } 139 140 size_t collection_threshold() const { 141 return ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold / 100; 142 } 143 144 bool collection_set_is(size_t r1) { return _collection_set_is(1, r1); } 145 bool collection_set_is(size_t r1, size_t r2) { return _collection_set_is(2, r1, r2); } 146 bool collection_set_is(size_t r1, size_t r2, size_t r3) { return _collection_set_is(3, r1, r2, r3); } 147 148 bool _collection_set_is(size_t count, ...) { 149 va_list args; 150 va_start(args, count); 151 EXPECT_EQ(count, _collection_set->count()); 152 bool result = true; 153 for (size_t i = 0; i < count; ++i) { 154 size_t index = va_arg(args, size_t); 155 if (!_collection_set->is_in(index)) { 156 result = false; 157 break; 158 } 159 } 160 va_end(args); 161 return result; 162 } 163 }; 164 165 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_regions) { 166 SKIP_IF_NOT_SHENANDOAH(); 167 168 _heuristics->prepare_for_old_collections(); 169 EXPECT_EQ(0U, _heuristics->coalesce_and_fill_candidates_count()); 170 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); 171 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 172 } 173 174 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_region_above_threshold) { 175 SKIP_IF_NOT_SHENANDOAH(); 176 177 // In this case, we have zero regions to add to the collection set, 178 // but we will have one region that must still be made parseable. 179 make_garbage_below_collection_threshold(10); 180 _heuristics->prepare_for_old_collections(); 181 EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count()); 182 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); 183 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 184 } 185 186 TEST_VM_F(ShenandoahOldHeuristicTest, select_one_old_region_above_threshold) { 187 SKIP_IF_NOT_SHENANDOAH(); 188 189 make_garbage_above_collection_threshold(10); 190 _heuristics->prepare_for_old_collections(); 191 EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count()); 192 EXPECT_EQ(1U, _heuristics->last_old_collection_candidate_index()); 193 EXPECT_EQ(1U, _heuristics->unprocessed_old_collection_candidates()); 194 } 195 196 TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) { 197 SKIP_IF_NOT_SHENANDOAH(); 198 199 size_t garbage = make_garbage_above_collection_threshold(10); 200 _heuristics->prepare_for_old_collections(); 201 _heuristics->prime_collection_set(_collection_set); 202 203 EXPECT_TRUE(collection_set_is(10UL)); 204 EXPECT_EQ(garbage, _collection_set->get_old_garbage()); 205 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 206 } 207 208 TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) { 209 SKIP_IF_NOT_SHENANDOAH(); 210 211 size_t g1 = make_garbage_above_collection_threshold(100); 212 size_t g2 = make_garbage_above_collection_threshold(101); 213 _heuristics->prepare_for_old_collections(); 214 _heuristics->prime_collection_set(_collection_set); 215 216 EXPECT_TRUE(collection_set_is(100UL, 101UL)); 217 EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage()); 218 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 219 } 220 221 TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) { 222 SKIP_IF_NOT_SHENANDOAH(); 223 224 size_t garbage = create_too_much_garbage_for_one_mixed_evacuation(); 225 _heuristics->prepare_for_old_collections(); 226 _heuristics->prime_collection_set(_collection_set); 227 228 EXPECT_LT(_collection_set->get_old_garbage(), garbage); 229 EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL); 230 } 231 232 TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) { 233 SKIP_IF_NOT_SHENANDOAH(); 234 235 // Create three old regions with enough garbage to be collected. 236 size_t g1 = make_garbage_above_collection_threshold(0); 237 size_t g2 = make_garbage_above_collection_threshold(1); 238 size_t g3 = make_garbage_above_collection_threshold(2); 239 240 // A region can be pinned when we chose collection set candidates. 241 make_pinned(1); 242 _heuristics->prepare_for_old_collections(); 243 244 // We only exclude pinned regions when we actually add regions to the collection set. 245 ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates()); 246 247 // Here the region is still pinned, so it cannot be added to the collection set. 248 _heuristics->prime_collection_set(_collection_set); 249 250 // The two unpinned regions should be added to the collection set and the pinned 251 // region should be retained at the front of the list of candidates as it would be 252 // likely to become unpinned by the next mixed collection cycle. 253 EXPECT_TRUE(collection_set_is(0UL, 2UL)); 254 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); 255 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 256 257 // Simulate another mixed collection after making region 1 unpinned. This time, 258 // the now unpinned region should be added to the collection set. 259 make_unpinned(1); 260 _collection_set->clear(); 261 _heuristics->prime_collection_set(_collection_set); 262 263 EXPECT_EQ(_collection_set->get_old_garbage(), g2); 264 EXPECT_TRUE(collection_set_is(1UL)); 265 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 266 } 267 268 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) { 269 SKIP_IF_NOT_SHENANDOAH(); 270 271 // Create three old regions with enough garbage to be collected. 272 size_t g1 = make_garbage_above_collection_threshold(0); 273 size_t g2 = make_garbage_above_collection_threshold(1); 274 size_t g3 = make_garbage_above_collection_threshold(2); 275 276 make_pinned(0); 277 _heuristics->prepare_for_old_collections(); 278 _heuristics->prime_collection_set(_collection_set); 279 280 EXPECT_TRUE(collection_set_is(1UL, 2UL)); 281 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 282 283 make_unpinned(0); 284 _collection_set->clear(); 285 _heuristics->prime_collection_set(_collection_set); 286 287 EXPECT_TRUE(collection_set_is(0UL)); 288 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 289 } 290 291 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) { 292 SKIP_IF_NOT_SHENANDOAH(); 293 294 // Create three old regions with enough garbage to be collected. 295 size_t g1 = make_garbage_above_collection_threshold(0); 296 size_t g2 = make_garbage_above_collection_threshold(1); 297 size_t g3 = make_garbage_above_collection_threshold(2); 298 299 make_pinned(2); 300 _heuristics->prepare_for_old_collections(); 301 _heuristics->prime_collection_set(_collection_set); 302 303 EXPECT_TRUE(collection_set_is(0UL, 1UL)); 304 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2); 305 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 306 307 make_unpinned(2); 308 _collection_set->clear(); 309 _heuristics->prime_collection_set(_collection_set); 310 311 EXPECT_TRUE(collection_set_is(2UL)); 312 EXPECT_EQ(_collection_set->get_old_garbage(), g3); 313 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 314 } 315 316 TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) { 317 SKIP_IF_NOT_SHENANDOAH(); 318 319 // Create three old regions with enough garbage to be collected. 320 size_t g1 = make_garbage_above_collection_threshold(0); 321 size_t g2 = make_garbage_above_collection_threshold(1); 322 size_t g3 = make_garbage_above_collection_threshold(2); 323 324 make_pinned(0); 325 make_pinned(2); 326 _heuristics->prepare_for_old_collections(); 327 _heuristics->prime_collection_set(_collection_set); 328 329 EXPECT_TRUE(collection_set_is(1UL)); 330 EXPECT_EQ(_collection_set->get_old_garbage(), g2); 331 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 2UL); 332 333 make_unpinned(0); 334 make_unpinned(2); 335 _collection_set->clear(); 336 _heuristics->prime_collection_set(_collection_set); 337 338 EXPECT_TRUE(collection_set_is(0UL, 2UL)); 339 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); 340 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 341 } 342 343 TEST_VM_F(ShenandoahOldHeuristicTest, all_candidates_are_pinned) { 344 SKIP_IF_NOT_SHENANDOAH(); 345 346 size_t g1 = make_garbage_above_collection_threshold(0); 347 size_t g2 = make_garbage_above_collection_threshold(1); 348 size_t g3 = make_garbage_above_collection_threshold(2); 349 350 make_pinned(0); 351 make_pinned(1); 352 make_pinned(2); 353 _heuristics->prepare_for_old_collections(); 354 _heuristics->prime_collection_set(_collection_set); 355 356 // In the case when all candidates are pinned, we want to abandon 357 // this set of mixed collection candidates so that another old collection 358 // can run. This is meant to defend against "bad" JNI code that permanently 359 // leaves an old region in the pinned state. 360 EXPECT_EQ(_collection_set->count(), 0UL); 361 EXPECT_EQ(old_generation_state(), ShenandoahOldGeneration::FILLING); 362 } 363 #undef SKIP_IF_NOT_SHENANDOAH