1 /*
2 * Copyright (c) 2002, 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
25 #ifndef SHARE_GC_PARALLEL_PSPROMOTIONMANAGER_INLINE_HPP
26 #define SHARE_GC_PARALLEL_PSPROMOTIONMANAGER_INLINE_HPP
27
28 #include "gc/parallel/psPromotionManager.hpp"
29
30 #include "gc/parallel/parallelScavengeHeap.hpp"
31 #include "gc/parallel/parMarkBitMap.inline.hpp"
32 #include "gc/parallel/psOldGen.hpp"
33 #include "gc/parallel/psPromotionLAB.inline.hpp"
34 #include "gc/parallel/psScavenge.hpp"
35 #include "gc/parallel/psStringDedup.hpp"
36 #include "gc/shared/continuationGCSupport.inline.hpp"
37 #include "gc/shared/taskqueue.inline.hpp"
38 #include "gc/shared/tlab_globals.hpp"
39 #include "logging/log.hpp"
40 #include "memory/iterator.inline.hpp"
41 #include "oops/access.inline.hpp"
42 #include "oops/oop.inline.hpp"
43 #include "runtime/orderAccess.hpp"
44 #include "runtime/prefetch.inline.hpp"
45 #include "utilities/copy.hpp"
46
47 inline PSPromotionManager* PSPromotionManager::manager_array(uint index) {
48 assert(_manager_array != nullptr, "access of null manager_array");
49 assert(index < ParallelGCThreads, "out of range manager_array access");
50 return &_manager_array[index];
51 }
52
53 template <class T>
54 inline void PSPromotionManager::claim_or_forward_depth(T* p) {
55 assert(ParallelScavengeHeap::heap()->is_in(p), "pointer outside heap");
56 T heap_oop = RawAccess<>::oop_load(p);
57 if (PSScavenge::is_obj_in_young(heap_oop)) {
58 oop obj = CompressedOops::decode_not_null(heap_oop);
59 assert(!PSScavenge::is_obj_in_to_space(obj), "revisiting object?");
60 Prefetch::write(obj->base_addr(), oopDesc::mark_offset_in_bytes());
61 claimed_stack_depth()->push(ScannerTask(p));
62 }
63 }
64
65 inline void PSPromotionManager::promotion_trace_event(oop new_obj, Klass* klass,
66 size_t obj_size,
67 uint age, bool tenured,
68 const PSPromotionLAB* lab) {
69 // Skip if memory allocation failed
70 if (new_obj != nullptr) {
71 const ParallelScavengeTracer* gc_tracer = PSScavenge::gc_tracer();
72
73 if (lab != nullptr) {
74 // Promotion of object through newly allocated PLAB
75 if (gc_tracer->should_report_promotion_in_new_plab_event()) {
76 size_t obj_bytes = obj_size * HeapWordSize;
77 size_t lab_size = lab->capacity();
78 gc_tracer->report_promotion_in_new_plab_event(klass, obj_bytes,
79 age, tenured, lab_size);
80 }
81 } else {
82 // Promotion of object directly to heap
83 if (gc_tracer->should_report_promotion_outside_plab_event()) {
84 size_t obj_bytes = obj_size * HeapWordSize;
85 gc_tracer->report_promotion_outside_plab_event(klass, obj_bytes,
86 age, tenured);
87 }
88 }
89 }
90 }
91
92 class PSPushContentsClosure: public BasicOopIterateClosure {
93 PSPromotionManager* _pm;
94 public:
95 PSPushContentsClosure(PSPromotionManager* pm) : BasicOopIterateClosure(PSScavenge::reference_processor()), _pm(pm) {}
96
97 template <typename T> void do_oop_work(T* p) {
98 _pm->claim_or_forward_depth(p);
99 }
100
101 virtual void do_oop(oop* p) { do_oop_work(p); }
102 virtual void do_oop(narrowOop* p) { do_oop_work(p); }
103 };
104
105 //
106 // This closure specialization will override the one that is defined in
107 // instanceRefKlass.inline.cpp. It swaps the order of oop_oop_iterate and
108 // oop_oop_iterate_ref_processing. Unfortunately G1 and Parallel behaves
109 // significantly better (especially in the Derby benchmark) using opposite
110 // order of these function calls.
111 //
112 template <>
113 inline void InstanceRefKlass::oop_oop_iterate_reverse<oop, PSPushContentsClosure>(oop obj, PSPushContentsClosure* closure) {
114 oop_oop_iterate_ref_processing<oop>(obj, closure);
115 InstanceKlass::oop_oop_iterate_reverse<oop>(obj, closure);
116 }
117
118 template <>
119 inline void InstanceRefKlass::oop_oop_iterate_reverse<narrowOop, PSPushContentsClosure>(oop obj, PSPushContentsClosure* closure) {
120 oop_oop_iterate_ref_processing<narrowOop>(obj, closure);
121 InstanceKlass::oop_oop_iterate_reverse<narrowOop>(obj, closure);
122 }
123
124 inline void PSPromotionManager::push_contents(oop obj) {
125 if (!obj->klass()->is_typeArray_klass()) {
126 PSPushContentsClosure pcc(this);
127 obj->oop_iterate_backwards(&pcc);
128 }
129 }
130
131 inline void PSPromotionManager::push_contents_bounded(oop obj, HeapWord* left, HeapWord* right) {
132 PSPushContentsClosure pcc(this);
133 obj->oop_iterate(&pcc, MemRegion(left, right));
134 }
135
136 template<bool promote_immediately>
137 inline oop PSPromotionManager::copy_to_survivor_space(oop o) {
138 assert(PSScavenge::is_obj_in_young(o), "precondition");
139 assert(!PSScavenge::is_obj_in_to_space(o), "precondition");
140
141 // NOTE! We must be very careful with any methods that access the mark
142 // in o. There may be multiple threads racing on it, and it may be forwarded
143 // at any time.
144 markWord m = o->mark();
145 if (!m.is_forwarded()) {
146 return copy_unmarked_to_survivor_space<promote_immediately>(o, m);
147 } else {
148 // Return the already installed forwardee.
149 return o->forwardee(m);
150 }
151 }
152
153 inline HeapWord* PSPromotionManager::allocate_in_young_gen(Klass* klass,
154 size_t obj_size,
155 uint age) {
156 HeapWord* result = _young_lab.allocate(obj_size);
157 if (result != nullptr) {
158 return result;
159 }
160 if (_young_gen_is_full) {
161 return nullptr;
162 }
163 // Do we allocate directly, or flush and refill?
164 if (obj_size > (YoungPLABSize / 2)) {
165 // Allocate this object directly
166 result = young_space()->cas_allocate(obj_size);
167 promotion_trace_event(cast_to_oop(result), klass, obj_size, age, false, nullptr);
168 } else {
169 // Flush and fill
170 _young_lab.flush();
171
172 HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize);
173 if (lab_base != nullptr) {
174 _young_lab.initialize(MemRegion(lab_base, YoungPLABSize));
175 // Try the young lab allocation again.
176 result = _young_lab.allocate(obj_size);
177 promotion_trace_event(cast_to_oop(result), klass, obj_size, age, false, &_young_lab);
178 } else {
179 _young_gen_is_full = true;
180 }
181 }
182 if (result == nullptr && !_young_gen_is_full && !_young_gen_has_alloc_failure) {
183 _young_gen_has_alloc_failure = true;
184 }
185 return result;
186 }
187
188 inline HeapWord* PSPromotionManager::allocate_in_old_gen(Klass* klass,
189 size_t obj_size,
190 uint age) {
191 #ifndef PRODUCT
192 if (ParallelScavengeHeap::heap()->promotion_should_fail()) {
193 return nullptr;
194 }
195 #endif // #ifndef PRODUCT
196
197 HeapWord* result = _old_lab.allocate(obj_size);
198 if (result != nullptr) {
199 return result;
200 }
201 if (_old_gen_is_full) {
202 return nullptr;
203 }
204 // Do we allocate directly, or flush and refill?
205 if (obj_size > (OldPLABSize / 2)) {
206 // Allocate this object directly
207 result = old_gen()->allocate(obj_size);
208 promotion_trace_event(cast_to_oop(result), klass, obj_size, age, true, nullptr);
209 } else {
210 // Flush and fill
211 _old_lab.flush();
212
213 HeapWord* lab_base = old_gen()->allocate(OldPLABSize);
214 if (lab_base != nullptr) {
215 _old_lab.initialize(MemRegion(lab_base, OldPLABSize));
216 // Try the old lab allocation again.
217 result = _old_lab.allocate(obj_size);
218 promotion_trace_event(cast_to_oop(result), klass, obj_size, age, true, &_old_lab);
219 }
220 }
221 if (result == nullptr) {
222 _old_gen_is_full = true;
223 }
224 return result;
225 }
226
227 //
228 // This method is pretty bulky. It would be nice to split it up
229 // into smaller submethods, but we need to be careful not to hurt
230 // performance.
231 //
232 template<bool promote_immediately>
233 inline oop PSPromotionManager::copy_unmarked_to_survivor_space(oop o,
234 markWord test_mark) {
235 HeapWord* new_obj_addr = nullptr;
236 bool new_obj_is_tenured = false;
237
238 // NOTE: With compact headers, it is not safe to load the Klass* from old, because
239 // that would access the mark-word, that might change at any time by concurrent
240 // workers.
241 // This mark word would refer to a forwardee, which may not yet have completed
242 // copying. Therefore we must load the Klass* from the mark-word that we already
243 // loaded. This is safe, because we only enter here if not yet forwarded.
244 assert(!test_mark.is_forwarded(), "precondition");
245 Klass* klass = UseCompactObjectHeaders
246 ? test_mark.klass()
247 : o->klass();
248
249 size_t new_obj_size = o->size_given_klass(klass);
250
251 // Find the objects age, MT safe.
252 uint age = (test_mark.has_displaced_mark_helper() /* o->has_displaced_mark() */) ?
253 test_mark.displaced_mark_helper().age() : test_mark.age();
254
255 if (!promote_immediately) {
256 // Try allocating obj in to-space (unless too old)
257 if (age < PSScavenge::tenuring_threshold()) {
258 new_obj_addr = allocate_in_young_gen(klass, new_obj_size, age);
259 }
260 }
261
262 // Otherwise try allocating obj tenured
263 if (new_obj_addr == nullptr) {
264 new_obj_addr = allocate_in_old_gen(klass, new_obj_size, age);
265 if (new_obj_addr == nullptr) {
266 return oop_promotion_failed(o, test_mark);
267 }
268 new_obj_is_tenured = true;
269 }
270
271 assert(new_obj_addr != nullptr, "allocation should have succeeded");
272
273 // Copy obj
274 Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), new_obj_addr, new_obj_size);
275
276 // Now we have to CAS in the header.
277 // Because the forwarding is done with memory_order_relaxed there is no
278 // ordering with the above copy. Clients that get the forwardee must not
279 // examine its contents without other synchronization, since the contents
280 // may not be up to date for them.
281 oop forwardee = o->forward_to_atomic(cast_to_oop(new_obj_addr), test_mark, memory_order_relaxed);
282 if (forwardee == nullptr) { // forwardee is null when forwarding is successful
283 // We won any races, we "own" this object.
284 oop new_obj = cast_to_oop(new_obj_addr);
285 assert(new_obj == o->forwardee(), "Sanity");
286
287 // Increment age if obj still in new generation. Now that
288 // we're dealing with a markWord that cannot change, it is
289 // okay to use the non mt safe oop methods.
290 if (!new_obj_is_tenured) {
291 new_obj->incr_age();
292 assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
293 }
294
295 ContinuationGCSupport::transform_stack_chunk(new_obj);
296
297 // Do the size comparison first with new_obj_size, which we
298 // already have. Hopefully, only a few objects are larger than
299 // _min_array_size_for_chunking, and most of them will be arrays.
300 // So, the objArray test would be very infrequent.
301 if (new_obj_size > _min_array_size_for_chunking &&
302 klass->is_refArray_klass() &&
303 PSChunkLargeArrays) {
304 push_objArray(o, new_obj);
305 } else {
306 // we'll just push its contents
307 push_contents(new_obj);
308
309 if (StringDedup::is_enabled_string(klass) &&
310 psStringDedup::is_candidate_from_evacuation(new_obj, new_obj_is_tenured)) {
311 _string_dedup_requests.add(o);
312 }
313 }
314 return new_obj;
315 } else {
316 // We lost, someone else "owns" this object.
317 assert(o->is_forwarded(), "Object must be forwarded if the cas failed.");
318 assert(o->forwardee() == forwardee, "invariant");
319
320 if (new_obj_is_tenured) {
321 _old_lab.unallocate_object(new_obj_addr, new_obj_size);
322 } else {
323 _young_lab.unallocate_object(new_obj_addr, new_obj_size);
324 }
325 return forwardee;
326 }
327 }
328
329 // Attempt to "claim" oop at p via CAS, push the new obj if successful
330 template <bool promote_immediately, class T>
331 inline void PSPromotionManager::copy_and_push_safe_barrier(T* p) {
332 assert(ParallelScavengeHeap::heap()->is_in_reserved(p), "precondition");
333
334 oop o = RawAccess<IS_NOT_NULL>::oop_load(p);
335 oop new_obj = copy_to_survivor_space<promote_immediately>(o);
336 RawAccess<IS_NOT_NULL>::oop_store(p, new_obj);
337
338 if (!PSScavenge::is_obj_in_young((HeapWord*)p) &&
339 PSScavenge::is_obj_in_young(new_obj)) {
340 PSScavenge::card_table()->inline_write_ref_field_gc(p);
341 }
342 }
343
344 inline void PSPromotionManager::process_popped_location_depth(ScannerTask task,
345 bool stolen) {
346 if (task.is_partial_array_state()) {
347 assert(PSChunkLargeArrays, "invariant");
348 process_array_chunk(task.to_partial_array_state(), stolen);
349 } else {
350 if (task.is_narrow_oop_ptr()) {
351 assert(UseCompressedOops, "Error");
352 copy_and_push_safe_barrier</*promote_immediately=*/false>(task.to_narrow_oop_ptr());
353 } else {
354 copy_and_push_safe_barrier</*promote_immediately=*/false>(task.to_oop_ptr());
355 }
356 }
357 }
358
359 inline bool PSPromotionManager::steal_depth(int queue_num, ScannerTask& t) {
360 return stack_array_depth()->steal(queue_num, t);
361 }
362
363 #endif // SHARE_GC_PARALLEL_PSPROMOTIONMANAGER_INLINE_HPP