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.
 21 
 22 #define SKIP_IF_NOT_SHENANDOAH() \
 23     if (!UseShenandoahGC) {      \
 24       tty->print_cr("skipped");  \
 25       return;                    \
 26     }
 27 
 28 class ShenandoahResetRegions : public ShenandoahHeapRegionClosure {
 29  public:
 30   virtual void heap_region_do(ShenandoahHeapRegion* region) override {
 31     if (!region->is_empty()) {
 32       region->make_trash();
 33       region->make_empty();
 34     }
 35     region->set_affiliation(FREE);
 36     region->clear_live_data();
 37     region->set_top(region->bottom());
 38   }
 39 };
 40 
 41 class ShenandoahOldHeuristicTest : public ::testing::Test {
 42  protected:
 43   ShenandoahHeap* _heap;
 44   ShenandoahOldHeuristics* _heuristics;
 45   ShenandoahCollectionSet* _collection_set;
 46 
 47   ShenandoahOldHeuristicTest()
 48     : _heap(ShenandoahHeap::heap()),
 49       _heuristics(_heap->old_heuristics()),
 50       _collection_set(_heap->collection_set()) {
 51     ShenandoahHeapLocker locker(_heap->lock());
 52     ShenandoahResetRegions reset;
 53     _heap->heap_region_iterate(&reset);
 54     _heap->set_old_evac_reserve(_heap->old_generation()->soft_max_capacity() / 4);
 55     _heuristics->abandon_collection_candidates();
 56     _collection_set->clear();
 57   }
 58 
 59   size_t make_garbage(size_t region_idx, size_t garbage_bytes) {
 60     ShenandoahHeapLocker locker(_heap->lock());
 61     ShenandoahHeapRegion* region = _heap->get_region(region_idx);
 62     region->make_regular_allocation(OLD_GENERATION);
 63     region->increase_live_data_alloc_words(1);
 64     region->set_top(region->bottom() + garbage_bytes / HeapWordSize);
 65     return region->garbage();
 66   }
 67 
 68   size_t create_too_much_garbage_for_one_mixed_evacuation() {
 69     size_t garbage_target = _heap->old_generation()->soft_max_capacity() / 2;
 70     size_t garbage_total = 0;
 71     size_t region_idx = 0;
 72     while (garbage_total < garbage_target && region_idx < _heap->num_regions()) {
 73       garbage_total += make_garbage_above_threshold(region_idx++);
 74     }
 75     return garbage_total;
 76   }
 77 
 78   void make_pinned(size_t region_idx) {
 79     ShenandoahHeapLocker locker(_heap->lock());
 80     ShenandoahHeapRegion* region = _heap->get_region(region_idx);
 81     region->record_pin();
 82     region->make_pinned();
 83   }
 84 
 85   void make_unpinned(size_t region_idx) {
 86     ShenandoahHeapLocker locker(_heap->lock());
 87     ShenandoahHeapRegion* region = _heap->get_region(region_idx);
 88     region->record_unpin();
 89     region->make_unpinned();
 90   }
 91 
 92   size_t make_garbage_below_threshold(size_t region_idx) {
 93     return make_garbage(region_idx, collection_threshold() - 100);
 94   }
 95 
 96   size_t make_garbage_above_threshold(size_t region_idx) {
 97     return make_garbage(region_idx, collection_threshold() + 100);
 98   }
 99 
100   size_t collection_threshold() const {
101     return ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold / 100;
102   }
103 };
104 
105 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_regions) {
106   SKIP_IF_NOT_SHENANDOAH();
107 
108   _heuristics->prepare_for_old_collections();
109   EXPECT_EQ(0U, _heuristics->last_old_region_index());
110   EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index());
111   EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
112 }
113 
114 TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_region_above_threshold) {
115   SKIP_IF_NOT_SHENANDOAH();
116 
117   // In this case, we have zero regions to add to the collection set,
118   // but we will have one region that must still be made parseable.
119   make_garbage_below_threshold(10);
120   _heuristics->prepare_for_old_collections();
121   EXPECT_EQ(1U, _heuristics->last_old_region_index());
122   EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index());
123   EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
124 }
125 
126 TEST_VM_F(ShenandoahOldHeuristicTest, select_one_old_region_above_threshold) {
127   SKIP_IF_NOT_SHENANDOAH();
128 
129   make_garbage_above_threshold(10);
130   _heuristics->prepare_for_old_collections();
131   EXPECT_EQ(1U, _heuristics->last_old_region_index());
132   EXPECT_EQ(1U, _heuristics->last_old_collection_candidate_index());
133   EXPECT_EQ(1U, _heuristics->unprocessed_old_collection_candidates());
134 }
135 
136 TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) {
137   SKIP_IF_NOT_SHENANDOAH();
138 
139   size_t garbage = make_garbage_above_threshold(10);
140   _heuristics->prepare_for_old_collections();
141   _heuristics->prime_collection_set(_collection_set);
142 
143   EXPECT_EQ(garbage, _collection_set->get_old_garbage());
144   EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
145 }
146 
147 TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) {
148   SKIP_IF_NOT_SHENANDOAH();
149 
150   size_t g1 = make_garbage_above_threshold(100);
151   size_t g2 = make_garbage_above_threshold(101);
152   _heuristics->prepare_for_old_collections();
153   _heuristics->prime_collection_set(_collection_set);
154 
155   EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage());
156   EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates());
157 }
158 
159 TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) {
160   SKIP_IF_NOT_SHENANDOAH();
161 
162   size_t garbage = create_too_much_garbage_for_one_mixed_evacuation();
163   _heuristics->prepare_for_old_collections();
164   _heuristics->prime_collection_set(_collection_set);
165 
166   EXPECT_LT(_collection_set->get_old_garbage(), garbage);
167   EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL);
168 }
169 
170 TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) {
171   SKIP_IF_NOT_SHENANDOAH();
172 
173   // Create three old regions with enough garbage to be collected.
174   size_t g1 = make_garbage_above_threshold(1);
175   size_t g2 = make_garbage_above_threshold(2);
176   size_t g3 = make_garbage_above_threshold(3);
177 
178   // A region can be pinned when we chose collection set candidates.
179   make_pinned(2);
180   _heuristics->prepare_for_old_collections();
181 
182   // We only excluded pinned regions when we actually add regions to the collection set.
183   ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates());
184 
185   // Here the region is still pinned, so it cannot be added to the collection set.
186   _heuristics->prime_collection_set(_collection_set);
187 
188   // The two unpinned regions should be added to the collection set and the pinned
189   // region should be retained at the front of the list of candidates as it would be
190   // likely to become unpinned by the next mixed collection cycle.
191   EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3);
192   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
193 
194   // Simulate another mixed collection after making region 2 unpinned. This time,
195   // the now unpinned region should be added to the collection set.
196   make_unpinned(2);
197   _collection_set->clear();
198   _heuristics->prime_collection_set(_collection_set);
199 
200   EXPECT_EQ(_collection_set->get_old_garbage(), g2);
201   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
202 }
203 
204 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) {
205   SKIP_IF_NOT_SHENANDOAH();
206 
207   // Create three old regions with enough garbage to be collected.
208   size_t g1 = make_garbage_above_threshold(1);
209   size_t g2 = make_garbage_above_threshold(2);
210   size_t g3 = make_garbage_above_threshold(3);
211 
212   make_pinned(1);
213   _heuristics->prepare_for_old_collections();
214   _heuristics->prime_collection_set(_collection_set);
215 
216   EXPECT_EQ(_collection_set->get_old_garbage(), g2 + g3);
217   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
218 
219   make_unpinned(1);
220   _collection_set->clear();
221   _heuristics->prime_collection_set(_collection_set);
222 
223   EXPECT_EQ(_collection_set->get_old_garbage(), g1);
224   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
225 }
226 
227 TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) {
228   SKIP_IF_NOT_SHENANDOAH();
229 
230   // Create three old regions with enough garbage to be collected.
231   size_t g1 = make_garbage_above_threshold(1);
232   size_t g2 = make_garbage_above_threshold(2);
233   size_t g3 = make_garbage_above_threshold(3);
234 
235   make_pinned(3);
236   _heuristics->prepare_for_old_collections();
237   _heuristics->prime_collection_set(_collection_set);
238 
239   EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2);
240   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL);
241 
242   make_unpinned(3);
243   _collection_set->clear();
244   _heuristics->prime_collection_set(_collection_set);
245 
246   EXPECT_EQ(_collection_set->get_old_garbage(), g3);
247   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
248 }
249 
250 TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) {
251   SKIP_IF_NOT_SHENANDOAH();
252 
253   // Create three old regions with enough garbage to be collected.
254   size_t g1 = make_garbage_above_threshold(1);
255   size_t g2 = make_garbage_above_threshold(2);
256   size_t g3 = make_garbage_above_threshold(3);
257 
258   make_pinned(1);
259   make_pinned(3);
260   _heuristics->prepare_for_old_collections();
261   _heuristics->prime_collection_set(_collection_set);
262 
263   EXPECT_EQ(_collection_set->get_old_garbage(), g2);
264   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 2UL);
265 
266   make_unpinned(1);
267   make_unpinned(3);
268   _collection_set->clear();
269   _heuristics->prime_collection_set(_collection_set);
270 
271   EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3);
272   EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL);
273 }