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