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->old_generation()->set_evacuation_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