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