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, 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