1 #include "precompiled.hpp" 2 #include "unittest.hpp" 3 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 4 #include "gc/shenandoah/shenandoahHeapRegion.hpp" 5 #include "gc/shenandoah/shenandoahGeneration.hpp" 6 #include "gc/shenandoah/shenandoahOldGeneration.hpp" 7 #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" 8 9 // These tests will all be skipped (unless Shenandoah becomes the default 10 // collector). To execute these tests, you must enable Shenandoah, which 11 // is done with: 12 // 13 // % _JAVA_OPTIONS="-XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational" make exploded-test TEST="gtest:Shenandoah*" 14 // 15 // Please note that these 'unit' tests are really integration tests and rely 16 // on the JVM being initialized. These tests manipulate the state of the 17 // collector in ways that are not compatible with a normal collection run. 18 // If these tests take longer than the minimum time between gc intervals - 19 // or, more likely, if you have them paused in a debugger longer than this 20 // interval - you can expect trouble. These tests will also not run in a build 21 // with asserts enabled because they use APIs that expect to run on a safepoint. 22 #ifdef ASSERT 23 #define SKIP_IF_NOT_SHENANDOAH() \ 24 tty->print_cr("skipped (debug build)" ); \ 25 return; 26 #else 27 #define SKIP_IF_NOT_SHENANDOAH() \ 28 if (!UseShenandoahGC) { \ 29 tty->print_cr("skipped"); \ 30 return; \ 31 } 32 #endif 33 34 class ShenandoahResetRegions : public ShenandoahHeapRegionClosure { 35 public: 36 virtual void heap_region_do(ShenandoahHeapRegion* region) override { 37 if (!region->is_empty()) { 38 region->make_trash(); 39 region->make_empty(); 40 } 41 region->set_affiliation(FREE); 42 region->clear_live_data(); 43 region->set_top(region->bottom()); 44 } 45 }; 46 47 class ShenandoahOldHeuristicTest : public ::testing::Test { 48 protected: 49 ShenandoahHeap* _heap; 50 ShenandoahOldHeuristics* _heuristics; 51 ShenandoahCollectionSet* _collection_set; 52 53 ShenandoahOldHeuristicTest() 54 : _heap(nullptr), 55 _heuristics(nullptr), 56 _collection_set(nullptr) { 57 SKIP_IF_NOT_SHENANDOAH(); 58 _heap = ShenandoahHeap::heap(); 59 _heuristics = _heap->old_heuristics(); 60 _collection_set = _heap->collection_set(); 61 ShenandoahHeapLocker locker(_heap->lock()); 62 ShenandoahResetRegions reset; 63 _heap->heap_region_iterate(&reset); 64 _heap->set_old_evac_reserve(_heap->old_generation()->soft_max_capacity() / 4); 65 _heuristics->abandon_collection_candidates(); 66 _collection_set->clear(); 67 } 68 69 size_t make_garbage(size_t region_idx, size_t garbage_bytes) { 70 ShenandoahHeapLocker locker(_heap->lock()); 71 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 72 region->make_regular_allocation(OLD_GENERATION); 73 region->increase_live_data_alloc_words(1); 74 region->set_top(region->bottom() + garbage_bytes / HeapWordSize); 75 return region->garbage(); 76 } 77 78 size_t create_too_much_garbage_for_one_mixed_evacuation() { 79 size_t garbage_target = _heap->old_generation()->soft_max_capacity() / 2; 80 size_t garbage_total = 0; 81 size_t region_idx = 0; 82 while (garbage_total < garbage_target && region_idx < _heap->num_regions()) { 83 garbage_total += make_garbage_above_threshold(region_idx++); 84 } 85 return garbage_total; 86 } 87 88 void make_pinned(size_t region_idx) { 89 ShenandoahHeapLocker locker(_heap->lock()); 90 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 91 region->record_pin(); 92 region->make_pinned(); 93 } 94 95 void make_unpinned(size_t region_idx) { 96 ShenandoahHeapLocker locker(_heap->lock()); 97 ShenandoahHeapRegion* region = _heap->get_region(region_idx); 98 region->record_unpin(); 99 region->make_unpinned(); 100 } 101 102 size_t make_garbage_below_threshold(size_t region_idx) { 103 return make_garbage(region_idx, collection_threshold() - 100); 104 } 105 106 size_t make_garbage_above_threshold(size_t region_idx) { 107 return make_garbage(region_idx, collection_threshold() + 100); 108 } 109 110 size_t collection_threshold() const { 111 return ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold / 100; 112 } 113 }; 114 115 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_regions) { 116 SKIP_IF_NOT_SHENANDOAH(); 117 118 _heuristics->prepare_for_old_collections(); 119 EXPECT_EQ(0U, _heuristics->last_old_region_index()); 120 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); 121 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 122 } 123 124 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_region_above_threshold) { 125 SKIP_IF_NOT_SHENANDOAH(); 126 127 // In this case, we have zero regions to add to the collection set, 128 // but we will have one region that must still be made parseable. 129 make_garbage_below_threshold(10); 130 _heuristics->prepare_for_old_collections(); 131 EXPECT_EQ(1U, _heuristics->last_old_region_index()); 132 EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); 133 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 134 } 135 136 TEST_VM_F(ShenandoahOldHeuristicTest, select_one_old_region_above_threshold) { 137 SKIP_IF_NOT_SHENANDOAH(); 138 139 make_garbage_above_threshold(10); 140 _heuristics->prepare_for_old_collections(); 141 EXPECT_EQ(1U, _heuristics->last_old_region_index()); 142 EXPECT_EQ(1U, _heuristics->last_old_collection_candidate_index()); 143 EXPECT_EQ(1U, _heuristics->unprocessed_old_collection_candidates()); 144 } 145 146 TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) { 147 SKIP_IF_NOT_SHENANDOAH(); 148 149 size_t garbage = make_garbage_above_threshold(10); 150 _heuristics->prepare_for_old_collections(); 151 _heuristics->prime_collection_set(_collection_set); 152 153 EXPECT_EQ(garbage, _collection_set->get_old_garbage()); 154 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 155 } 156 157 TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) { 158 SKIP_IF_NOT_SHENANDOAH(); 159 160 size_t g1 = make_garbage_above_threshold(100); 161 size_t g2 = make_garbage_above_threshold(101); 162 _heuristics->prepare_for_old_collections(); 163 _heuristics->prime_collection_set(_collection_set); 164 165 EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage()); 166 EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); 167 } 168 169 TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) { 170 SKIP_IF_NOT_SHENANDOAH(); 171 172 size_t garbage = create_too_much_garbage_for_one_mixed_evacuation(); 173 _heuristics->prepare_for_old_collections(); 174 _heuristics->prime_collection_set(_collection_set); 175 176 EXPECT_LT(_collection_set->get_old_garbage(), garbage); 177 EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL); 178 } 179 180 TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) { 181 SKIP_IF_NOT_SHENANDOAH(); 182 183 // Create three old regions with enough garbage to be collected. 184 size_t g1 = make_garbage_above_threshold(1); 185 size_t g2 = make_garbage_above_threshold(2); 186 size_t g3 = make_garbage_above_threshold(3); 187 188 // A region can be pinned when we chose collection set candidates. 189 make_pinned(2); 190 _heuristics->prepare_for_old_collections(); 191 192 // We only excluded pinned regions when we actually add regions to the collection set. 193 ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates()); 194 195 // Here the region is still pinned, so it cannot be added to the collection set. 196 _heuristics->prime_collection_set(_collection_set); 197 198 // The two unpinned regions should be added to the collection set and the pinned 199 // region should be retained at the front of the list of candidates as it would be 200 // likely to become unpinned by the next mixed collection cycle. 201 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); 202 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 203 204 // Simulate another mixed collection after making region 2 unpinned. This time, 205 // the now unpinned region should be added to the collection set. 206 make_unpinned(2); 207 _collection_set->clear(); 208 _heuristics->prime_collection_set(_collection_set); 209 210 EXPECT_EQ(_collection_set->get_old_garbage(), g2); 211 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 212 } 213 214 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) { 215 SKIP_IF_NOT_SHENANDOAH(); 216 217 // Create three old regions with enough garbage to be collected. 218 size_t g1 = make_garbage_above_threshold(1); 219 size_t g2 = make_garbage_above_threshold(2); 220 size_t g3 = make_garbage_above_threshold(3); 221 222 make_pinned(1); 223 _heuristics->prepare_for_old_collections(); 224 _heuristics->prime_collection_set(_collection_set); 225 226 EXPECT_EQ(_collection_set->get_old_garbage(), g2 + g3); 227 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 228 229 make_unpinned(1); 230 _collection_set->clear(); 231 _heuristics->prime_collection_set(_collection_set); 232 233 EXPECT_EQ(_collection_set->get_old_garbage(), g1); 234 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 235 } 236 237 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) { 238 SKIP_IF_NOT_SHENANDOAH(); 239 240 // Create three old regions with enough garbage to be collected. 241 size_t g1 = make_garbage_above_threshold(1); 242 size_t g2 = make_garbage_above_threshold(2); 243 size_t g3 = make_garbage_above_threshold(3); 244 245 make_pinned(3); 246 _heuristics->prepare_for_old_collections(); 247 _heuristics->prime_collection_set(_collection_set); 248 249 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2); 250 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); 251 252 make_unpinned(3); 253 _collection_set->clear(); 254 _heuristics->prime_collection_set(_collection_set); 255 256 EXPECT_EQ(_collection_set->get_old_garbage(), g3); 257 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 258 } 259 260 TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) { 261 SKIP_IF_NOT_SHENANDOAH(); 262 263 // Create three old regions with enough garbage to be collected. 264 size_t g1 = make_garbage_above_threshold(1); 265 size_t g2 = make_garbage_above_threshold(2); 266 size_t g3 = make_garbage_above_threshold(3); 267 268 make_pinned(1); 269 make_pinned(3); 270 _heuristics->prepare_for_old_collections(); 271 _heuristics->prime_collection_set(_collection_set); 272 273 EXPECT_EQ(_collection_set->get_old_garbage(), g2); 274 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 2UL); 275 276 make_unpinned(1); 277 make_unpinned(3); 278 _collection_set->clear(); 279 _heuristics->prime_collection_set(_collection_set); 280 281 EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); 282 EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); 283 } 284 285 #undef SKIP_IF_NOT_SHENANDOAH