1 | /* |
2 | * Copyright (c) 2002, 2019, 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/parallelScavengeHeap.hpp" |
29 | #include "gc/parallel/parMarkBitMap.inline.hpp" |
30 | #include "gc/parallel/psOldGen.hpp" |
31 | #include "gc/parallel/psPromotionLAB.inline.hpp" |
32 | #include "gc/parallel/psPromotionManager.hpp" |
33 | #include "gc/parallel/psScavenge.inline.hpp" |
34 | #include "gc/shared/taskqueue.inline.hpp" |
35 | #include "logging/log.hpp" |
36 | #include "memory/iterator.inline.hpp" |
37 | #include "oops/access.inline.hpp" |
38 | #include "oops/oop.inline.hpp" |
39 | |
40 | inline PSPromotionManager* PSPromotionManager::manager_array(uint index) { |
41 | assert(_manager_array != NULL, "access of NULL manager_array" ); |
42 | assert(index <= ParallelGCThreads, "out of range manager_array access" ); |
43 | return &_manager_array[index]; |
44 | } |
45 | |
46 | template <class T> |
47 | inline void PSPromotionManager::push_depth(T* p) { |
48 | claimed_stack_depth()->push(p); |
49 | } |
50 | |
51 | template <class T> |
52 | inline void PSPromotionManager::claim_or_forward_internal_depth(T* p) { |
53 | if (p != NULL) { // XXX: error if p != NULL here |
54 | oop o = RawAccess<IS_NOT_NULL>::oop_load(p); |
55 | if (o->is_forwarded()) { |
56 | o = o->forwardee(); |
57 | // Card mark |
58 | if (PSScavenge::is_obj_in_young(o)) { |
59 | PSScavenge::card_table()->inline_write_ref_field_gc(p, o); |
60 | } |
61 | RawAccess<IS_NOT_NULL>::oop_store(p, o); |
62 | } else { |
63 | push_depth(p); |
64 | } |
65 | } |
66 | } |
67 | |
68 | template <class T> |
69 | inline void PSPromotionManager::claim_or_forward_depth(T* p) { |
70 | assert(should_scavenge(p, true), "revisiting object?" ); |
71 | assert(ParallelScavengeHeap::heap()->is_in(p), "pointer outside heap" ); |
72 | |
73 | claim_or_forward_internal_depth(p); |
74 | } |
75 | |
76 | inline void PSPromotionManager::promotion_trace_event(oop new_obj, oop old_obj, |
77 | size_t obj_size, |
78 | uint age, bool tenured, |
79 | const PSPromotionLAB* lab) { |
80 | // Skip if memory allocation failed |
81 | if (new_obj != NULL) { |
82 | const ParallelScavengeTracer* gc_tracer = PSScavenge::gc_tracer(); |
83 | |
84 | if (lab != NULL) { |
85 | // Promotion of object through newly allocated PLAB |
86 | if (gc_tracer->should_report_promotion_in_new_plab_event()) { |
87 | size_t obj_bytes = obj_size * HeapWordSize; |
88 | size_t lab_size = lab->capacity(); |
89 | gc_tracer->report_promotion_in_new_plab_event(old_obj->klass(), obj_bytes, |
90 | age, tenured, lab_size); |
91 | } |
92 | } else { |
93 | // Promotion of object directly to heap |
94 | if (gc_tracer->should_report_promotion_outside_plab_event()) { |
95 | size_t obj_bytes = obj_size * HeapWordSize; |
96 | gc_tracer->report_promotion_outside_plab_event(old_obj->klass(), obj_bytes, |
97 | age, tenured); |
98 | } |
99 | } |
100 | } |
101 | } |
102 | |
103 | class PSPushContentsClosure: public BasicOopIterateClosure { |
104 | PSPromotionManager* _pm; |
105 | public: |
106 | PSPushContentsClosure(PSPromotionManager* pm) : BasicOopIterateClosure(PSScavenge::reference_processor()), _pm(pm) {} |
107 | |
108 | template <typename T> void do_oop_nv(T* p) { |
109 | if (PSScavenge::should_scavenge(p)) { |
110 | _pm->claim_or_forward_depth(p); |
111 | } |
112 | } |
113 | |
114 | virtual void do_oop(oop* p) { do_oop_nv(p); } |
115 | virtual void do_oop(narrowOop* p) { do_oop_nv(p); } |
116 | |
117 | // Don't use the oop verification code in the oop_oop_iterate framework. |
118 | debug_only(virtual bool should_verify_oops() { return false; }) |
119 | }; |
120 | |
121 | // |
122 | // This closure specialization will override the one that is defined in |
123 | // instanceRefKlass.inline.cpp. It swaps the order of oop_oop_iterate and |
124 | // oop_oop_iterate_ref_processing. Unfortunately G1 and Parallel behaves |
125 | // significantly better (especially in the Derby benchmark) using opposite |
126 | // order of these function calls. |
127 | // |
128 | template <> |
129 | inline void InstanceRefKlass::oop_oop_iterate_reverse<oop, PSPushContentsClosure>(oop obj, PSPushContentsClosure* closure) { |
130 | oop_oop_iterate_ref_processing<oop>(obj, closure); |
131 | InstanceKlass::oop_oop_iterate_reverse<oop>(obj, closure); |
132 | } |
133 | |
134 | template <> |
135 | inline void InstanceRefKlass::oop_oop_iterate_reverse<narrowOop, PSPushContentsClosure>(oop obj, PSPushContentsClosure* closure) { |
136 | oop_oop_iterate_ref_processing<narrowOop>(obj, closure); |
137 | InstanceKlass::oop_oop_iterate_reverse<narrowOop>(obj, closure); |
138 | } |
139 | |
140 | inline void PSPromotionManager::push_contents(oop obj) { |
141 | if (!obj->klass()->is_typeArray_klass()) { |
142 | PSPushContentsClosure pcc(this); |
143 | obj->oop_iterate_backwards(&pcc); |
144 | } |
145 | } |
146 | // |
147 | // This method is pretty bulky. It would be nice to split it up |
148 | // into smaller submethods, but we need to be careful not to hurt |
149 | // performance. |
150 | // |
151 | template<bool promote_immediately> |
152 | inline oop PSPromotionManager::copy_to_survivor_space(oop o) { |
153 | assert(should_scavenge(&o), "Sanity" ); |
154 | |
155 | oop new_obj = NULL; |
156 | |
157 | // NOTE! We must be very careful with any methods that access the mark |
158 | // in o. There may be multiple threads racing on it, and it may be forwarded |
159 | // at any time. Do not use oop methods for accessing the mark! |
160 | markOop test_mark = o->mark_raw(); |
161 | |
162 | // The same test as "o->is_forwarded()" |
163 | if (!test_mark->is_marked()) { |
164 | bool new_obj_is_tenured = false; |
165 | size_t new_obj_size = o->size(); |
166 | |
167 | // Find the objects age, MT safe. |
168 | uint age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ? |
169 | test_mark->displaced_mark_helper()->age() : test_mark->age(); |
170 | |
171 | if (!promote_immediately) { |
172 | // Try allocating obj in to-space (unless too old) |
173 | if (age < PSScavenge::tenuring_threshold()) { |
174 | new_obj = (oop) _young_lab.allocate(new_obj_size); |
175 | if (new_obj == NULL && !_young_gen_is_full) { |
176 | // Do we allocate directly, or flush and refill? |
177 | if (new_obj_size > (YoungPLABSize / 2)) { |
178 | // Allocate this object directly |
179 | new_obj = (oop)young_space()->cas_allocate(new_obj_size); |
180 | promotion_trace_event(new_obj, o, new_obj_size, age, false, NULL); |
181 | } else { |
182 | // Flush and fill |
183 | _young_lab.flush(); |
184 | |
185 | HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize); |
186 | if (lab_base != NULL) { |
187 | _young_lab.initialize(MemRegion(lab_base, YoungPLABSize)); |
188 | // Try the young lab allocation again. |
189 | new_obj = (oop) _young_lab.allocate(new_obj_size); |
190 | promotion_trace_event(new_obj, o, new_obj_size, age, false, &_young_lab); |
191 | } else { |
192 | _young_gen_is_full = true; |
193 | } |
194 | } |
195 | } |
196 | } |
197 | } |
198 | |
199 | // Otherwise try allocating obj tenured |
200 | if (new_obj == NULL) { |
201 | #ifndef PRODUCT |
202 | if (ParallelScavengeHeap::heap()->promotion_should_fail()) { |
203 | return oop_promotion_failed(o, test_mark); |
204 | } |
205 | #endif // #ifndef PRODUCT |
206 | |
207 | new_obj = (oop) _old_lab.allocate(new_obj_size); |
208 | new_obj_is_tenured = true; |
209 | |
210 | if (new_obj == NULL) { |
211 | if (!_old_gen_is_full) { |
212 | // Do we allocate directly, or flush and refill? |
213 | if (new_obj_size > (OldPLABSize / 2)) { |
214 | // Allocate this object directly |
215 | new_obj = (oop)old_gen()->cas_allocate(new_obj_size); |
216 | promotion_trace_event(new_obj, o, new_obj_size, age, true, NULL); |
217 | } else { |
218 | // Flush and fill |
219 | _old_lab.flush(); |
220 | |
221 | HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize); |
222 | if(lab_base != NULL) { |
223 | #ifdef ASSERT |
224 | // Delay the initialization of the promotion lab (plab). |
225 | // This exposes uninitialized plabs to card table processing. |
226 | if (GCWorkerDelayMillis > 0) { |
227 | os::sleep(Thread::current(), GCWorkerDelayMillis, false); |
228 | } |
229 | #endif |
230 | _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); |
231 | // Try the old lab allocation again. |
232 | new_obj = (oop) _old_lab.allocate(new_obj_size); |
233 | promotion_trace_event(new_obj, o, new_obj_size, age, true, &_old_lab); |
234 | } |
235 | } |
236 | } |
237 | |
238 | // This is the promotion failed test, and code handling. |
239 | // The code belongs here for two reasons. It is slightly |
240 | // different than the code below, and cannot share the |
241 | // CAS testing code. Keeping the code here also minimizes |
242 | // the impact on the common case fast path code. |
243 | |
244 | if (new_obj == NULL) { |
245 | _old_gen_is_full = true; |
246 | return oop_promotion_failed(o, test_mark); |
247 | } |
248 | } |
249 | } |
250 | |
251 | assert(new_obj != NULL, "allocation should have succeeded" ); |
252 | |
253 | // Copy obj |
254 | Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size); |
255 | |
256 | // Now we have to CAS in the header. |
257 | // Make copy visible to threads reading the forwardee. |
258 | if (o->cas_forward_to(new_obj, test_mark, memory_order_release)) { |
259 | // We won any races, we "own" this object. |
260 | assert(new_obj == o->forwardee(), "Sanity" ); |
261 | |
262 | // Increment age if obj still in new generation. Now that |
263 | // we're dealing with a markOop that cannot change, it is |
264 | // okay to use the non mt safe oop methods. |
265 | if (!new_obj_is_tenured) { |
266 | new_obj->incr_age(); |
267 | assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj" ); |
268 | } |
269 | |
270 | // Do the size comparison first with new_obj_size, which we |
271 | // already have. Hopefully, only a few objects are larger than |
272 | // _min_array_size_for_chunking, and most of them will be arrays. |
273 | // So, the is->objArray() test would be very infrequent. |
274 | if (new_obj_size > _min_array_size_for_chunking && |
275 | new_obj->is_objArray() && |
276 | PSChunkLargeArrays) { |
277 | // we'll chunk it |
278 | oop* const masked_o = mask_chunked_array_oop(o); |
279 | push_depth(masked_o); |
280 | TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes); |
281 | } else { |
282 | // we'll just push its contents |
283 | push_contents(new_obj); |
284 | } |
285 | } else { |
286 | // We lost, someone else "owns" this object |
287 | guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed." ); |
288 | |
289 | // Try to deallocate the space. If it was directly allocated we cannot |
290 | // deallocate it, so we have to test. If the deallocation fails, |
291 | // overwrite with a filler object. |
292 | if (new_obj_is_tenured) { |
293 | if (!_old_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) { |
294 | CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size); |
295 | } |
296 | } else if (!_young_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) { |
297 | CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size); |
298 | } |
299 | |
300 | // don't update this before the unallocation! |
301 | // Using acquire though consume would be accurate for accessing new_obj. |
302 | new_obj = o->forwardee_acquire(); |
303 | } |
304 | } else { |
305 | assert(o->is_forwarded(), "Sanity" ); |
306 | new_obj = o->forwardee_acquire(); |
307 | } |
308 | |
309 | // This code must come after the CAS test, or it will print incorrect |
310 | // information. |
311 | log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}" , |
312 | should_scavenge(&new_obj) ? "copying" : "tenuring" , |
313 | new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); |
314 | |
315 | return new_obj; |
316 | } |
317 | |
318 | // Attempt to "claim" oop at p via CAS, push the new obj if successful |
319 | // This version tests the oop* to make sure it is within the heap before |
320 | // attempting marking. |
321 | template <class T, bool promote_immediately> |
322 | inline void PSPromotionManager::copy_and_push_safe_barrier(T* p) { |
323 | assert(should_scavenge(p, true), "revisiting object?" ); |
324 | |
325 | oop o = RawAccess<IS_NOT_NULL>::oop_load(p); |
326 | oop new_obj = o->is_forwarded() |
327 | ? o->forwardee() |
328 | : copy_to_survivor_space<promote_immediately>(o); |
329 | |
330 | // This code must come after the CAS test, or it will print incorrect |
331 | // information. |
332 | if (log_develop_is_enabled(Trace, gc, scavenge) && o->is_forwarded()) { |
333 | log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}" , |
334 | "forwarding" , |
335 | new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); |
336 | } |
337 | |
338 | RawAccess<IS_NOT_NULL>::oop_store(p, new_obj); |
339 | |
340 | // We cannot mark without test, as some code passes us pointers |
341 | // that are outside the heap. These pointers are either from roots |
342 | // or from metadata. |
343 | if ((!PSScavenge::is_obj_in_young((HeapWord*)p)) && |
344 | ParallelScavengeHeap::heap()->is_in_reserved(p)) { |
345 | if (PSScavenge::is_obj_in_young(new_obj)) { |
346 | PSScavenge::card_table()->inline_write_ref_field_gc(p, new_obj); |
347 | } |
348 | } |
349 | } |
350 | |
351 | inline void PSPromotionManager::process_popped_location_depth(StarTask p) { |
352 | if (is_oop_masked(p)) { |
353 | assert(PSChunkLargeArrays, "invariant" ); |
354 | oop const old = unmask_chunked_array_oop(p); |
355 | process_array_chunk(old); |
356 | } else { |
357 | if (p.is_narrow()) { |
358 | assert(UseCompressedOops, "Error" ); |
359 | copy_and_push_safe_barrier<narrowOop, /*promote_immediately=*/false>(p); |
360 | } else { |
361 | copy_and_push_safe_barrier<oop, /*promote_immediately=*/false>(p); |
362 | } |
363 | } |
364 | } |
365 | |
366 | inline bool PSPromotionManager::steal_depth(int queue_num, StarTask& t) { |
367 | return stack_array_depth()->steal(queue_num, t); |
368 | } |
369 | |
370 | #if TASKQUEUE_STATS |
371 | void PSPromotionManager::record_steal(StarTask& p) { |
372 | if (is_oop_masked(p)) { |
373 | ++_masked_steals; |
374 | } |
375 | } |
376 | #endif // TASKQUEUE_STATS |
377 | |
378 | #endif // SHARE_GC_PARALLEL_PSPROMOTIONMANAGER_INLINE_HPP |
379 | |