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