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