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