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 
 27 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 28 #include "gc/shenandoah/mode/shenandoahMode.hpp"
 29 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
 30 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
 31 
 32 #define SKIP_IF_NOT_SHENANDOAH() \
 33   if (!(UseShenandoahGC && ShenandoahHeap::heap()->mode()->is_generational())) {                 \
 34     tty->print_cr("skipped (run with -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational)");  \
 35     return;                                                                                      \
 36   }
 37 
 38 
 39 class ShenandoahOldGenerationTest : public ::testing::Test {
 40 protected:
 41   static const size_t INITIAL_PLAB_SIZE;
 42   static const size_t INITIAL_PLAB_PROMOTED;
 43 
 44   ShenandoahOldGeneration* old;
 45 
 46   ShenandoahOldGenerationTest()
 47     : old(nullptr)
 48   {
 49   }
 50 
 51   void SetUp() override {
 52     SKIP_IF_NOT_SHENANDOAH();
 53 
 54     ShenandoahHeap::heap()->lock()->lock(false);
 55 
 56     old = new ShenandoahOldGeneration(8, 1024 * 1024, 1024);
 57     old->set_promoted_reserve(512 * HeapWordSize);
 58     old->expend_promoted(256 * HeapWordSize);
 59     old->set_evacuation_reserve(512 * HeapWordSize);
 60 
 61     Thread* thread = Thread::current();
 62     ShenandoahThreadLocalData::reset_plab_promoted(thread);
 63     ShenandoahThreadLocalData::disable_plab_promotions(thread);
 64     ShenandoahThreadLocalData::set_plab_actual_size(thread, INITIAL_PLAB_SIZE);
 65     ShenandoahThreadLocalData::add_to_plab_promoted(thread, INITIAL_PLAB_PROMOTED);
 66   }
 67 
 68   void TearDown() override {
 69     if (UseShenandoahGC) {
 70       ShenandoahHeap::heap()->lock()->unlock();
 71       delete old;
 72     }
 73   }
 74 
 75   static bool promotions_enabled() {
 76     return ShenandoahThreadLocalData::allow_plab_promotions(Thread::current());
 77   }
 78 
 79   static size_t plab_size() {
 80     return ShenandoahThreadLocalData::get_plab_actual_size(Thread::current());
 81   }
 82 
 83   static size_t plab_promoted() {
 84     return ShenandoahThreadLocalData::get_plab_promoted(Thread::current());
 85   }
 86 };
 87 
 88 const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_SIZE = 42;
 89 const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_PROMOTED = 128;
 90 
 91 TEST_VM_F(ShenandoahOldGenerationTest, test_can_promote) {
 92   SKIP_IF_NOT_SHENANDOAH();
 93   EXPECT_TRUE(old->can_promote(128 * HeapWordSize)) << "Should have room to promote";
 94   EXPECT_FALSE(old->can_promote(384 * HeapWordSize)) << "Should not have room to promote";
 95 }
 96 
 97 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_promotion) {
 98   SKIP_IF_NOT_SHENANDOAH();
 99   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
100   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to promote";
101 }
102 
103 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_evacuation) {
104   SKIP_IF_NOT_SHENANDOAH();
105   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384);
106   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotions";
107   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate";
108 }
109 
110 TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_plab) {
111   SKIP_IF_NOT_SHENANDOAH();
112   // Simulate having exhausted the evacuation reserve when request is too big to be promoted
113   old->set_evacuation_reserve(0);
114   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384);
115   EXPECT_FALSE(old->can_allocate(req)) << "No room for promotions or evacuations";
116 }
117 
118 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_for_shared_evacuation) {
119   SKIP_IF_NOT_SHENANDOAH();
120   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, false);
121   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion";
122   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate shared (even though evacuation reserve is smaller than request)";
123 }
124 
125 TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_for_shared_promotion) {
126   SKIP_IF_NOT_SHENANDOAH();
127   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, true);
128   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion";
129   EXPECT_FALSE(old->can_allocate(req)) << "No room to promote, should fall back to evacuation in young gen";
130 }
131 
132 TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted) {
133   SKIP_IF_NOT_SHENANDOAH();
134   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
135 
136   // simulate the allocation
137   req.set_actual_size(128);
138 
139   size_t actual_size = req.actual_size() * HeapWordSize;
140   EXPECT_TRUE(old->can_promote(actual_size)) << "Should have room for promotion";
141 
142   size_t expended_before = old->get_promoted_expended();
143   old->configure_plab_for_current_thread(req);
144   size_t expended_after = old->get_promoted_expended();
145   EXPECT_EQ(expended_before + actual_size, expended_after) << "Should expend promotion reserve";
146   EXPECT_EQ(plab_promoted(), 0UL) << "Nothing promoted yet";
147   EXPECT_EQ(plab_size(), actual_size) << "New plab should be able to hold this much promotion";
148   EXPECT_TRUE(promotions_enabled()) << "Plab should be available for promotions";
149 }
150 
151 TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserve) {
152   SKIP_IF_NOT_SHENANDOAH();
153   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
154 
155   // simulate an allocation that exceeds the promotion reserve after allocation
156   req.set_actual_size(384);
157   EXPECT_FALSE(old->can_promote(req.actual_size() * HeapWordSize)) << "Should have room for promotion";
158 
159   size_t expended_before = old->get_promoted_expended();
160   old->configure_plab_for_current_thread(req);
161   size_t expended_after = old->get_promoted_expended();
162 
163   EXPECT_EQ(expended_before, expended_after) << "Did not promote, should not expend promotion";
164   EXPECT_EQ(plab_promoted(), 0UL) << "Cannot promote in new plab";
165   EXPECT_EQ(plab_size(), 0UL) << "Should not have space for promotions";
166   EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations";
167 }
168 
169 TEST_VM_F(ShenandoahOldGenerationTest, test_shared_expends_promoted_but_does_not_change_plab) {
170   SKIP_IF_NOT_SHENANDOAH();
171   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, true);
172   req.set_actual_size(128);
173   size_t actual_size = req.actual_size() * HeapWordSize;
174 
175   size_t expended_before = old->get_promoted_expended();
176   old->configure_plab_for_current_thread(req);
177   size_t expended_after = old->get_promoted_expended();
178 
179   EXPECT_EQ(expended_before + actual_size, expended_after) << "Shared promotion still expends promotion";
180   EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Shared promotion should not count in plab";
181   EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Shared promotion should not change size of plab";
182   EXPECT_FALSE(promotions_enabled());
183 }
184 
185 TEST_VM_F(ShenandoahOldGenerationTest, test_shared_evacuation_has_no_side_effects) {
186   SKIP_IF_NOT_SHENANDOAH();
187   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, false);
188   req.set_actual_size(128);
189 
190   size_t expended_before = old->get_promoted_expended();
191   old->configure_plab_for_current_thread(req);
192   size_t expended_after = old->get_promoted_expended();
193 
194   EXPECT_EQ(expended_before, expended_after) << "Not a promotion, should not expend promotion reserve";
195   EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Not a plab, should not have touched plab";
196   EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Not a plab, should not have touched plab";
197   EXPECT_FALSE(promotions_enabled());
198 }
199 
200 #undef SKIP_IF_NOT_SHENANDOAH