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