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);
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