1 /*
2 * Copyright (c) 2015, 2025, Oracle and/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 "gc/shared/gc_globals.hpp"
25 #include "gc/z/zGeneration.inline.hpp"
26 #include "gc/z/zGlobals.hpp"
27 #include "gc/z/zPage.inline.hpp"
28 #include "gc/z/zPageAge.hpp"
29 #include "gc/z/zRememberedSet.inline.hpp"
30 #include "utilities/align.hpp"
31 #include "utilities/debug.hpp"
32
33 ZPage::ZPage(ZPageType type, ZPageAge age, const ZVirtualMemory& vmem, ZMultiPartitionTracker* multi_partition_tracker, uint32_t partition_id)
34 : _type(type),
35 _generation_id(/* set in reset */),
36 _age(/* set in reset */),
37 _seqnum(/* set in reset */),
38 _seqnum_other(/* set in reset */),
39 _single_partition_id(partition_id),
40 _virtual(vmem),
41 _top(to_zoffset_end(start())),
42 _livemap(object_max_count()),
43 _remembered_set(),
44 _multi_partition_tracker(multi_partition_tracker),
45 _relocate_promoted(false) {
46 assert(!_virtual.is_null(), "Should not be null");
47 assert((_type == ZPageType::small && size() == ZPageSizeSmall) ||
48 (_type == ZPageType::medium && ZPageSizeMediumMin <= size() && size() <= ZPageSizeMediumMax) ||
49 (_type == ZPageType::large && is_aligned(size(), ZGranuleSize)),
50 "Page type/size mismatch");
51 reset(age);
52
53 if (is_old()) {
54 remset_alloc();
55 }
56 }
57
58 ZPage::ZPage(ZPageType type, ZPageAge age, const ZVirtualMemory& vmem, uint32_t partition_id)
59 : ZPage(type, age, vmem, nullptr /* multi_partition_tracker */, partition_id) {}
60
61 ZPage::ZPage(ZPageType type, ZPageAge age, const ZVirtualMemory& vmem, ZMultiPartitionTracker* multi_partition_tracker)
62 : ZPage(type, age, vmem, multi_partition_tracker, -1u /* partition_id */) {}
63
64 ZPage* ZPage::clone_for_promotion() const {
65 assert(_age != ZPageAge::old, "must be used for promotion");
66 // Only copy type and memory layouts, and also update _top. Let the rest be
67 // lazily reconstructed when needed.
68 ZPage* const page = new ZPage(_type, ZPageAge::old, _virtual, _multi_partition_tracker, _single_partition_id);
69 page->_top = _top;
70
71 return page;
72 }
73
74 bool ZPage::allows_raw_null() const {
75 return is_young() && !AtomicAccess::load(&_relocate_promoted);
76 }
77
78 void ZPage::set_is_relocate_promoted() {
79 AtomicAccess::store(&_relocate_promoted, true);
80 }
81
82 ZGeneration* ZPage::generation() {
83 return ZGeneration::generation(_generation_id);
84 }
85
86 const ZGeneration* ZPage::generation() const {
87 return ZGeneration::generation(_generation_id);
88 }
89
90 void ZPage::reset_seqnum() {
91 AtomicAccess::store(&_seqnum, generation()->seqnum());
92 AtomicAccess::store(&_seqnum_other, ZGeneration::generation(_generation_id == ZGenerationId::young ? ZGenerationId::old : ZGenerationId::young)->seqnum());
93 }
94
95 void ZPage::remset_alloc() {
96 // Remsets should only be allocated/initialized once and only for old pages.
97 assert(!_remembered_set.is_initialized(), "Should not be initialized");
98 assert(is_old(), "Only old pages need a remset");
99
100 _remembered_set.initialize(size());
101 }
102
103 ZPage* ZPage::reset(ZPageAge age) {
104 _age = age;
105
106 _generation_id = age == ZPageAge::old
107 ? ZGenerationId::old
108 : ZGenerationId::young;
109
110 reset_seqnum();
111
112 return this;
113 }
114
115 void ZPage::reset_livemap() {
116 _livemap.reset();
117 }
118
119 void ZPage::reset_top_for_allocation() {
120 _top = to_zoffset_end(start());
121 }
122
123 class ZFindBaseOopClosure : public ObjectClosure {
124 private:
125 volatile zpointer* _p;
126 oop _result;
127
128 public:
129 ZFindBaseOopClosure(volatile zpointer* p)
130 : _p(p),
131 _result(nullptr) {}
132
133 virtual void do_object(oop obj) {
134 const uintptr_t p_int = reinterpret_cast<uintptr_t>(_p);
135 const uintptr_t base_int = cast_from_oop<uintptr_t>(obj);
136 const uintptr_t end_int = base_int + wordSize * obj->size();
137 if (p_int >= base_int && p_int < end_int) {
138 _result = obj;
139 }
140 }
141
142 oop result() const { return _result; }
143 };
144
145 bool ZPage::is_remset_cleared_current() const {
146 return _remembered_set.is_cleared_current();
147 }
148
149 bool ZPage::is_remset_cleared_previous() const {
150 return _remembered_set.is_cleared_previous();
151 }
152
153 void ZPage::verify_remset_cleared_current() const {
154 if (ZVerifyRemembered && !is_remset_cleared_current()) {
155 fatal_msg(" current remset bits should be cleared");
156 }
157 }
158
159 void ZPage::verify_remset_cleared_previous() const {
160 if (ZVerifyRemembered && !is_remset_cleared_previous()) {
161 fatal_msg(" previous remset bits should be cleared");
162 }
163 }
164
165 void ZPage::clear_remset_previous() {
166 _remembered_set.clear_previous();
167 }
168
169 void ZPage::swap_remset_bitmaps() {
170 _remembered_set.swap_remset_bitmaps();
171 }
172
173 void* ZPage::remset_current() {
174 return _remembered_set.current();
175 }
176
177 void ZPage::print_on_msg(outputStream* st, const char* msg) const {
178 st->print_cr("%-6s " PTR_FORMAT " " PTR_FORMAT " " PTR_FORMAT " %s/%-4u %s%s%s%s",
179 type_to_string(), untype(start()), untype(top()), untype(end()),
180 is_young() ? "Y" : "O",
181 seqnum(),
182 is_relocatable() ? " Relocatable" : "",
183 is_allocating() ? " Allocating" : "",
184 is_allocating() && msg != nullptr ? " " : "",
185 msg != nullptr ? msg : "");
186 }
187
188 void ZPage::print_on(outputStream* st) const {
189 print_on_msg(st, nullptr);
190 }
191
192 void ZPage::print() const {
193 print_on(tty);
194 }
195
196 void ZPage::verify_live(uint32_t live_objects, size_t live_bytes, uint32_t no_move_expand_count, bool in_place) const {
197 if (!in_place) {
198 // In-place relocation has changed the page to allocating
199 assert_zpage_mark_state();
200 }
201 guarantee(live_objects == _livemap.live_objects(), "Invalid number of live objects");
202 // live_bytes is computed from TO-space sizes. Expanding objects (is_hashed_not_expanded at
203 // mark time, expand_for_hash) contribute one extra HeapWord each in TO space. Non-moving
204 // in-place objects that would expand but stayed at the same address did NOT expand, so they
205 // are excluded via no_move_expand_count.
206 const size_t expected_live_bytes = _livemap.live_bytes()
207 + ((size_t)(_livemap.will_expand_objects() - no_move_expand_count)) * HeapWordSize;
208 guarantee(live_bytes == expected_live_bytes, "Invalid number of live bytes");
209 }
210
211 void ZPage::fatal_msg(const char* msg) const {
212 stringStream ss;
213 print_on_msg(&ss, msg);
214 fatal("%s", ss.base());
215 }