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()->set_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