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 }