1 /*
2 * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2020, 2021, Red Hat, Inc. and/or its affiliates.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26 #include "precompiled.hpp"
27 #include "classfile/javaClasses.hpp"
28 #include "gc/shared/workerThread.hpp"
29 #include "gc/shenandoah/shenandoahOopClosures.inline.hpp"
30 #include "gc/shenandoah/shenandoahReferenceProcessor.hpp"
31 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
32 #include "gc/shenandoah/shenandoahUtils.hpp"
33 #include "runtime/atomic.hpp"
34 #include "logging/log.hpp"
35
36 static ReferenceType reference_type(oop reference) {
37 return InstanceKlass::cast(reference->klass())->reference_type();
38 }
39
40 static const char* reference_type_name(ReferenceType type) {
41 switch (type) {
42 case REF_SOFT:
43 return "Soft";
44
45 case REF_WEAK:
46 return "Weak";
47
48 case REF_FINAL:
49 return "Final";
50
51 case REF_PHANTOM:
52 return "Phantom";
53
54 default:
55 ShouldNotReachHere();
56 return nullptr;
57 }
58 }
59
60 template <typename T>
61 static void set_oop_field(T* field, oop value);
62
63 template <>
64 void set_oop_field<oop>(oop* field, oop value) {
65 *field = value;
66 }
67
68 template <>
69 void set_oop_field<narrowOop>(narrowOop* field, oop value) {
70 *field = CompressedOops::encode(value);
71 }
72
73 static oop lrb(oop obj) {
74 if (obj != nullptr && ShenandoahHeap::heap()->marking_context()->is_marked(obj)) {
75 return ShenandoahBarrierSet::barrier_set()->load_reference_barrier(obj);
76 } else {
77 return obj;
78 }
79 }
80
81 template <typename T>
82 static volatile T* reference_referent_addr(oop reference) {
83 return (volatile T*)java_lang_ref_Reference::referent_addr_raw(reference);
84 }
85
86 template <typename T>
87 static oop reference_referent(oop reference) {
88 T heap_oop = Atomic::load(reference_referent_addr<T>(reference));
89 return CompressedOops::decode(heap_oop);
90 }
91
92 static void reference_clear_referent(oop reference) {
93 java_lang_ref_Reference::clear_referent_raw(reference);
94 }
95
96 template <typename T>
97 static T* reference_discovered_addr(oop reference) {
98 return reinterpret_cast<T*>(java_lang_ref_Reference::discovered_addr_raw(reference));
99 }
100
101 template <typename T>
102 static oop reference_discovered(oop reference) {
103 T heap_oop = *reference_discovered_addr<T>(reference);
104 return lrb(CompressedOops::decode(heap_oop));
105 }
106
107 template <typename T>
108 static void reference_set_discovered(oop reference, oop discovered);
109
240 }
241
242 bool ShenandoahReferenceProcessor::is_softly_live(oop reference, ReferenceType type) const {
243 if (type != REF_SOFT) {
244 // Not a SoftReference
245 return false;
246 }
247
248 // Ask SoftReference policy
249 const jlong clock = java_lang_ref_SoftReference::clock();
250 assert(clock != 0, "Clock not initialized");
251 assert(_soft_reference_policy != nullptr, "Policy not initialized");
252 return !_soft_reference_policy->should_clear_reference(reference, clock);
253 }
254
255 template <typename T>
256 bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType type) const {
257 T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference);
258 T heap_oop = RawAccess<>::oop_load(referent_addr);
259 oop referent = CompressedOops::decode(heap_oop);
260
261 if (is_inactive<T>(reference, referent, type)) {
262 log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference));
263 return false;
264 }
265
266 if (is_strongly_live(referent)) {
267 log_trace(gc,ref)("Reference strongly live: " PTR_FORMAT, p2i(reference));
268 return false;
269 }
270
271 if (is_softly_live(reference, type)) {
272 log_trace(gc,ref)("Reference softly live: " PTR_FORMAT, p2i(reference));
273 return false;
274 }
275
276 return true;
277 }
278
279 template <typename T>
280 bool ShenandoahReferenceProcessor::should_drop(oop reference, ReferenceType type) const {
281 const oop referent = reference_referent<T>(reference);
282 if (referent == nullptr) {
283 // Reference has been cleared, by a call to Reference.enqueue()
284 // or Reference.clear() from the application, which means we
285 // should drop the reference.
286 return true;
287 }
288
289 // Check if the referent is still alive, in which case we should
290 // drop the reference.
291 if (type == REF_PHANTOM) {
292 return ShenandoahHeap::heap()->complete_marking_context()->is_marked(referent);
293 } else {
294 return ShenandoahHeap::heap()->complete_marking_context()->is_marked_strong(referent);
295 }
296 }
297
298 template <typename T>
299 void ShenandoahReferenceProcessor::make_inactive(oop reference, ReferenceType type) const {
300 if (type == REF_FINAL) {
301 // Don't clear referent. It is needed by the Finalizer thread to make the call
302 // to finalize(). A FinalReference is instead made inactive by self-looping the
303 // next field. An application can't call FinalReference.enqueue(), so there is
304 // no race to worry about when setting the next field.
305 assert(reference_next<T>(reference) == nullptr, "Already inactive");
306 assert(ShenandoahHeap::heap()->marking_context()->is_marked(reference_referent<T>(reference)), "only make inactive final refs with alive referents");
307 reference_set_next(reference, reference);
308 } else {
309 // Clear referent
310 reference_clear_referent(reference);
311 }
312 }
313
314 template <typename T>
315 bool ShenandoahReferenceProcessor::discover(oop reference, ReferenceType type, uint worker_id) {
316 if (!should_discover<T>(reference, type)) {
317 // Not discovered
318 return false;
319 }
320
321 if (reference_discovered<T>(reference) != nullptr) {
322 // Already discovered. This can happen if the reference is marked finalizable first, and then strong,
323 // in which case it will be seen 2x by marking.
324 log_trace(gc,ref)("Reference already discovered: " PTR_FORMAT, p2i(reference));
325 return true;
326 }
327
328 if (type == REF_FINAL) {
329 ShenandoahMarkRefsSuperClosure* cl = _ref_proc_thread_locals[worker_id].mark_closure();
330 bool weak = cl->is_weak();
331 cl->set_weak(true);
332 if (UseCompressedOops) {
333 cl->do_oop(reinterpret_cast<narrowOop*>(java_lang_ref_Reference::referent_addr_raw(reference)));
334 } else {
335 cl->do_oop(reinterpret_cast<oop*>(java_lang_ref_Reference::referent_addr_raw(reference)));
336 }
337 cl->set_weak(weak);
338 }
339
340 // Add reference to discovered list
341 ShenandoahRefProcThreadLocal& refproc_data = _ref_proc_thread_locals[worker_id];
342 oop discovered_head = refproc_data.discovered_list_head<T>();
343 if (discovered_head == nullptr) {
344 // Self-loop tail of list. We distinguish discovered from not-discovered references by looking at their
345 // discovered field: if it is null, then it is not-yet discovered, otherwise it is discovered
346 discovered_head = reference;
347 }
348 if (reference_cas_discovered<T>(reference, discovered_head)) {
349 refproc_data.set_discovered_list_head<T>(reference);
350 assert(refproc_data.discovered_list_head<T>() == reference, "reference must be new discovered head");
351 log_trace(gc, ref)("Discovered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
352 _ref_proc_thread_locals[worker_id].inc_discovered(type);
353 }
354 return true;
355 }
356
357 bool ShenandoahReferenceProcessor::discover_reference(oop reference, ReferenceType type) {
358 if (!RegisterReferences) {
359 // Reference processing disabled
360 return false;
361 }
362
363 log_trace(gc, ref)("Encountered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
364 uint worker_id = WorkerThread::worker_id();
365 _ref_proc_thread_locals[worker_id].inc_encountered(type);
366
367 if (UseCompressedOops) {
368 return discover<narrowOop>(reference, type, worker_id);
369 } else {
370 return discover<oop>(reference, type, worker_id);
371 }
372 }
373
374 template <typename T>
375 oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) {
376 log_trace(gc, ref)("Dropped Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
377
378 #ifdef ASSERT
379 oop referent = reference_referent<T>(reference);
380 assert(referent == nullptr || ShenandoahHeap::heap()->marking_context()->is_marked(referent),
381 "only drop references with alive referents");
382 #endif
383
384 // Unlink and return next in list
385 oop next = reference_discovered<T>(reference);
386 reference_set_discovered<T>(reference, nullptr);
387 return next;
388 }
389
390 template <typename T>
391 T* ShenandoahReferenceProcessor::keep(oop reference, ReferenceType type, uint worker_id) {
392 log_trace(gc, ref)("Enqueued Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
393
394 // Update statistics
395 _ref_proc_thread_locals[worker_id].inc_enqueued(type);
396
397 // Make reference inactive
398 make_inactive<T>(reference, type);
399
400 // Return next in list
401 return reference_discovered_addr<T>(reference);
402 }
403
404 template <typename T>
405 void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id) {
406 log_trace(gc, ref)("Processing discovered list #%u : " PTR_FORMAT, worker_id, p2i(refproc_data.discovered_list_head<T>()));
418 break;
419 }
420 log_trace(gc, ref)("Processing reference: " PTR_FORMAT, p2i(reference));
421 const ReferenceType type = reference_type(reference);
422
423 if (should_drop<T>(reference, type)) {
424 set_oop_field(p, drop<T>(reference, type));
425 } else {
426 p = keep<T>(reference, type, worker_id);
427 }
428
429 const oop discovered = lrb(reference_discovered<T>(reference));
430 if (reference == discovered) {
431 // Reset terminating self-loop to null
432 reference_set_discovered<T>(reference, oop(nullptr));
433 break;
434 }
435 }
436
437 // Prepend discovered references to internal pending list
438 if (!CompressedOops::is_null(*list)) {
439 oop head = lrb(CompressedOops::decode_not_null(*list));
440 shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier);
441 oop prev = Atomic::xchg(&_pending_list, head);
442 RawAccess<>::oop_store(p, prev);
443 if (prev == nullptr) {
444 // First to prepend to list, record tail
445 _pending_list_tail = reinterpret_cast<void*>(p);
446 }
447
448 // Clear discovered list
449 set_oop_field(list, oop(nullptr));
450 }
451 }
452
453 void ShenandoahReferenceProcessor::work() {
454 // Process discovered references
455 uint max_workers = ShenandoahHeap::heap()->max_workers();
456 uint worker_id = Atomic::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1;
457 while (worker_id < max_workers) {
458 if (UseCompressedOops) {
459 process_references<narrowOop>(_ref_proc_thread_locals[worker_id], worker_id);
460 } else {
461 process_references<oop>(_ref_proc_thread_locals[worker_id], worker_id);
462 }
494 void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent) {
495
496 Atomic::release_store_fence(&_iterate_discovered_list_id, 0U);
497
498 // Process discovered lists
499 ShenandoahReferenceProcessorTask task(phase, concurrent, this);
500 workers->run_task(&task);
501
502 // Update SoftReference clock
503 soft_reference_update_clock();
504
505 // Collect, log and trace statistics
506 collect_statistics();
507
508 enqueue_references(concurrent);
509 }
510
511 void ShenandoahReferenceProcessor::enqueue_references_locked() {
512 // Prepend internal pending list to external pending list
513 shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier);
514 if (UseCompressedOops) {
515 *reinterpret_cast<narrowOop*>(_pending_list_tail) = CompressedOops::encode(Universe::swap_reference_pending_list(_pending_list));
516 } else {
517 *reinterpret_cast<oop*>(_pending_list_tail) = Universe::swap_reference_pending_list(_pending_list);
518 }
519 }
520
521 void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) {
522 if (_pending_list == nullptr) {
523 // Nothing to enqueue
524 return;
525 }
526
527 if (!concurrent) {
528 // When called from mark-compact or degen-GC, the locking is done by the VMOperation,
529 enqueue_references_locked();
530 } else {
531 // Heap_lock protects external pending list
532 MonitorLocker ml(Heap_lock);
533
534 enqueue_references_locked();
535
536 // Notify ReferenceHandler thread
537 ml.notify_all();
538 }
539
540 // Reset internal pending list
541 _pending_list = nullptr;
542 _pending_list_tail = &_pending_list;
543 }
544
545 template<typename T>
546 void ShenandoahReferenceProcessor::clean_discovered_list(T* list) {
584 for (uint i = 0; i < max_workers; i++) {
585 for (size_t type = 0; type < reference_type_count; type++) {
586 encountered[type] += _ref_proc_thread_locals[i].encountered((ReferenceType)type);
587 discovered[type] += _ref_proc_thread_locals[i].discovered((ReferenceType)type);
588 enqueued[type] += _ref_proc_thread_locals[i].enqueued((ReferenceType)type);
589 }
590 }
591
592 _stats = ReferenceProcessorStats(discovered[REF_SOFT],
593 discovered[REF_WEAK],
594 discovered[REF_FINAL],
595 discovered[REF_PHANTOM]);
596
597 log_info(gc,ref)("Encountered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
598 encountered[REF_SOFT], encountered[REF_WEAK], encountered[REF_FINAL], encountered[REF_PHANTOM]);
599 log_info(gc,ref)("Discovered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
600 discovered[REF_SOFT], discovered[REF_WEAK], discovered[REF_FINAL], discovered[REF_PHANTOM]);
601 log_info(gc,ref)("Enqueued references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
602 enqueued[REF_SOFT], enqueued[REF_WEAK], enqueued[REF_FINAL], enqueued[REF_PHANTOM]);
603 }
604
|
1 /*
2 * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2020, 2021, Red Hat, Inc. and/or its affiliates.
4 * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 *
7 * This code is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 only, as
9 * published by the Free Software Foundation.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 */
26
27 #include "precompiled.hpp"
28 #include "classfile/javaClasses.hpp"
29 #include "gc/shared/workerThread.hpp"
30 #include "gc/shenandoah/shenandoahGeneration.hpp"
31 #include "gc/shenandoah/shenandoahOopClosures.inline.hpp"
32 #include "gc/shenandoah/shenandoahReferenceProcessor.hpp"
33 #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp"
34 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
35 #include "gc/shenandoah/shenandoahUtils.hpp"
36 #include "runtime/atomic.hpp"
37 #include "logging/log.hpp"
38
39 static ReferenceType reference_type(oop reference) {
40 return InstanceKlass::cast(reference->klass())->reference_type();
41 }
42
43 static const char* reference_type_name(ReferenceType type) {
44 switch (type) {
45 case REF_SOFT:
46 return "Soft";
47
48 case REF_WEAK:
49 return "Weak";
50
51 case REF_FINAL:
52 return "Final";
53
54 case REF_PHANTOM:
55 return "Phantom";
56
57 default:
58 ShouldNotReachHere();
59 return nullptr;
60 }
61 }
62
63 template <typename T>
64 static void card_mark_barrier(T* field, oop value) {
65 assert(ShenandoahCardBarrier, "Card-mark barrier should be on");
66 ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
67 assert(heap->is_in_or_null(value), "Should be in heap");
68 if (heap->is_in_old(field) && heap->is_in_young(value)) {
69 // For Shenandoah, each generation collects all the _referents_ that belong to the
70 // collected generation. We can end up with discovered lists that contain a mixture
71 // of old and young _references_. These references are linked together through the
72 // discovered field in java.lang.Reference. In some cases, creating or editing this
73 // list may result in the creation of _new_ old-to-young pointers which must dirty
74 // the corresponding card. Failing to do this may cause heap verification errors and
75 // lead to incorrect GC behavior.
76 heap->old_generation()->mark_card_as_dirty(field);
77 }
78 }
79
80 template <typename T>
81 static void set_oop_field(T* field, oop value);
82
83 template <>
84 void set_oop_field<oop>(oop* field, oop value) {
85 *field = value;
86 if (ShenandoahCardBarrier) {
87 card_mark_barrier(field, value);
88 }
89 }
90
91 template <>
92 void set_oop_field<narrowOop>(narrowOop* field, oop value) {
93 *field = CompressedOops::encode(value);
94 if (ShenandoahCardBarrier) {
95 card_mark_barrier(field, value);
96 }
97 }
98
99 static oop lrb(oop obj) {
100 if (obj != nullptr && ShenandoahHeap::heap()->marking_context()->is_marked(obj)) {
101 return ShenandoahBarrierSet::barrier_set()->load_reference_barrier(obj);
102 } else {
103 return obj;
104 }
105 }
106
107 template <typename T>
108 static volatile T* reference_referent_addr(oop reference) {
109 return (volatile T*)java_lang_ref_Reference::referent_addr_raw(reference);
110 }
111
112 inline oop reference_coop_decode_raw(narrowOop v) {
113 return CompressedOops::is_null(v) ? nullptr : CompressedOops::decode_raw(v);
114 }
115
116 inline oop reference_coop_decode_raw(oop v) {
117 return v;
118 }
119
120 // Raw referent, it can be dead. You cannot treat it as oop without additional safety
121 // checks, this is why it is HeapWord*. The decoding uses a special-case inlined
122 // CompressedOops::decode method that bypasses normal oop-ness checks.
123 template <typename T>
124 static HeapWord* reference_referent_raw(oop reference) {
125 T raw_oop = Atomic::load(reference_referent_addr<T>(reference));
126 return cast_from_oop<HeapWord*>(reference_coop_decode_raw(raw_oop));
127 }
128
129 static void reference_clear_referent(oop reference) {
130 java_lang_ref_Reference::clear_referent_raw(reference);
131 }
132
133 template <typename T>
134 static T* reference_discovered_addr(oop reference) {
135 return reinterpret_cast<T*>(java_lang_ref_Reference::discovered_addr_raw(reference));
136 }
137
138 template <typename T>
139 static oop reference_discovered(oop reference) {
140 T heap_oop = *reference_discovered_addr<T>(reference);
141 return lrb(CompressedOops::decode(heap_oop));
142 }
143
144 template <typename T>
145 static void reference_set_discovered(oop reference, oop discovered);
146
277 }
278
279 bool ShenandoahReferenceProcessor::is_softly_live(oop reference, ReferenceType type) const {
280 if (type != REF_SOFT) {
281 // Not a SoftReference
282 return false;
283 }
284
285 // Ask SoftReference policy
286 const jlong clock = java_lang_ref_SoftReference::clock();
287 assert(clock != 0, "Clock not initialized");
288 assert(_soft_reference_policy != nullptr, "Policy not initialized");
289 return !_soft_reference_policy->should_clear_reference(reference, clock);
290 }
291
292 template <typename T>
293 bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType type) const {
294 T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference);
295 T heap_oop = RawAccess<>::oop_load(referent_addr);
296 oop referent = CompressedOops::decode(heap_oop);
297 ShenandoahHeap* heap = ShenandoahHeap::heap();
298
299 if (is_inactive<T>(reference, referent, type)) {
300 log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference));
301 return false;
302 }
303
304 if (is_strongly_live(referent)) {
305 log_trace(gc,ref)("Reference strongly live: " PTR_FORMAT, p2i(reference));
306 return false;
307 }
308
309 if (is_softly_live(reference, type)) {
310 log_trace(gc,ref)("Reference softly live: " PTR_FORMAT, p2i(reference));
311 return false;
312 }
313
314 if (!heap->is_in_active_generation(referent)) {
315 log_trace(gc,ref)("Referent outside of active generation: " PTR_FORMAT, p2i(referent));
316 return false;
317 }
318
319 return true;
320 }
321
322 template <typename T>
323 bool ShenandoahReferenceProcessor::should_drop(oop reference, ReferenceType type) const {
324 HeapWord* raw_referent = reference_referent_raw<T>(reference);
325 if (raw_referent == nullptr) {
326 // Reference has been cleared, by a call to Reference.enqueue()
327 // or Reference.clear() from the application, which means we
328 // should drop the reference.
329 return true;
330 }
331
332 // Check if the referent is still alive, in which case we should
333 // drop the reference.
334 if (type == REF_PHANTOM) {
335 return ShenandoahHeap::heap()->complete_marking_context()->is_marked(raw_referent);
336 } else {
337 return ShenandoahHeap::heap()->complete_marking_context()->is_marked_strong(raw_referent);
338 }
339 }
340
341 template <typename T>
342 void ShenandoahReferenceProcessor::make_inactive(oop reference, ReferenceType type) const {
343 if (type == REF_FINAL) {
344 // Don't clear referent. It is needed by the Finalizer thread to make the call
345 // to finalize(). A FinalReference is instead made inactive by self-looping the
346 // next field. An application can't call FinalReference.enqueue(), so there is
347 // no race to worry about when setting the next field.
348 assert(reference_next<T>(reference) == nullptr, "Already inactive");
349 assert(ShenandoahHeap::heap()->marking_context()->is_marked(reference_referent_raw<T>(reference)), "only make inactive final refs with alive referents");
350 reference_set_next(reference, reference);
351 } else {
352 // Clear referent
353 reference_clear_referent(reference);
354 }
355 }
356
357 template <typename T>
358 bool ShenandoahReferenceProcessor::discover(oop reference, ReferenceType type, uint worker_id) {
359 if (!should_discover<T>(reference, type)) {
360 // Not discovered
361 return false;
362 }
363
364 if (reference_discovered<T>(reference) != nullptr) {
365 // Already discovered. This can happen if the reference is marked finalizable first, and then strong,
366 // in which case it will be seen 2x by marking.
367 log_trace(gc,ref)("Reference already discovered: " PTR_FORMAT, p2i(reference));
368 return true;
369 }
370
371 if (type == REF_FINAL) {
372 ShenandoahMarkRefsSuperClosure* cl = _ref_proc_thread_locals[worker_id].mark_closure();
373 bool weak = cl->is_weak();
374 cl->set_weak(true);
375 if (UseCompressedOops) {
376 cl->do_oop(reinterpret_cast<narrowOop*>(java_lang_ref_Reference::referent_addr_raw(reference)));
377 } else {
378 cl->do_oop(reinterpret_cast<oop*>(java_lang_ref_Reference::referent_addr_raw(reference)));
379 }
380 cl->set_weak(weak);
381 }
382
383 // Add reference to discovered list
384 // Each worker thread has a private copy of refproc_data, which includes a private discovered list. This means
385 // there's no risk that a different worker thread will try to manipulate my discovered list head while I'm making
386 // reference the head of my discovered list.
387 ShenandoahRefProcThreadLocal& refproc_data = _ref_proc_thread_locals[worker_id];
388 oop discovered_head = refproc_data.discovered_list_head<T>();
389 if (discovered_head == nullptr) {
390 // Self-loop tail of list. We distinguish discovered from not-discovered references by looking at their
391 // discovered field: if it is null, then it is not-yet discovered, otherwise it is discovered
392 discovered_head = reference;
393 }
394 if (reference_cas_discovered<T>(reference, discovered_head)) {
395 // We successfully set this reference object's next pointer to discovered_head. This marks reference as discovered.
396 // If reference_cas_discovered fails, that means some other worker thread took credit for discovery of this reference,
397 // and that other thread will place reference on its discovered list, so I can ignore reference.
398
399 // In case we have created an interesting pointer, mark the remembered set card as dirty.
400 if (ShenandoahCardBarrier) {
401 T* addr = reinterpret_cast<T*>(java_lang_ref_Reference::discovered_addr_raw(reference));
402 card_mark_barrier(addr, discovered_head);
403 }
404
405 // Make the discovered_list_head point to reference.
406 refproc_data.set_discovered_list_head<T>(reference);
407 assert(refproc_data.discovered_list_head<T>() == reference, "reference must be new discovered head");
408 log_trace(gc, ref)("Discovered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
409 _ref_proc_thread_locals[worker_id].inc_discovered(type);
410 }
411 return true;
412 }
413
414 bool ShenandoahReferenceProcessor::discover_reference(oop reference, ReferenceType type) {
415 if (!RegisterReferences) {
416 // Reference processing disabled
417 return false;
418 }
419
420 log_trace(gc, ref)("Encountered Reference: " PTR_FORMAT " (%s, %s)",
421 p2i(reference), reference_type_name(type), ShenandoahHeap::heap()->heap_region_containing(reference)->affiliation_name());
422 uint worker_id = WorkerThread::worker_id();
423 _ref_proc_thread_locals[worker_id].inc_encountered(type);
424
425 if (UseCompressedOops) {
426 return discover<narrowOop>(reference, type, worker_id);
427 } else {
428 return discover<oop>(reference, type, worker_id);
429 }
430 }
431
432 template <typename T>
433 oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) {
434 log_trace(gc, ref)("Dropped Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
435
436 ShenandoahHeap* heap = ShenandoahHeap::heap();
437 HeapWord* referent = reference_referent_raw<T>(reference);
438 assert(referent == nullptr || heap->marking_context()->is_marked(referent), "only drop references with alive referents");
439
440 // Unlink and return next in list
441 oop next = reference_discovered<T>(reference);
442 reference_set_discovered<T>(reference, nullptr);
443 // When this reference was discovered, it would not have been marked. If it ends up surviving
444 // the cycle, we need to dirty the card if the reference is old and the referent is young. Note
445 // that if the reference is not dropped, then its pointer to the referent will be nulled before
446 // evacuation begins so card does not need to be dirtied.
447 if (ShenandoahCardBarrier) {
448 card_mark_barrier(cast_from_oop<HeapWord*>(reference), cast_to_oop(referent));
449 }
450 return next;
451 }
452
453 template <typename T>
454 T* ShenandoahReferenceProcessor::keep(oop reference, ReferenceType type, uint worker_id) {
455 log_trace(gc, ref)("Enqueued Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type));
456
457 // Update statistics
458 _ref_proc_thread_locals[worker_id].inc_enqueued(type);
459
460 // Make reference inactive
461 make_inactive<T>(reference, type);
462
463 // Return next in list
464 return reference_discovered_addr<T>(reference);
465 }
466
467 template <typename T>
468 void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id) {
469 log_trace(gc, ref)("Processing discovered list #%u : " PTR_FORMAT, worker_id, p2i(refproc_data.discovered_list_head<T>()));
481 break;
482 }
483 log_trace(gc, ref)("Processing reference: " PTR_FORMAT, p2i(reference));
484 const ReferenceType type = reference_type(reference);
485
486 if (should_drop<T>(reference, type)) {
487 set_oop_field(p, drop<T>(reference, type));
488 } else {
489 p = keep<T>(reference, type, worker_id);
490 }
491
492 const oop discovered = lrb(reference_discovered<T>(reference));
493 if (reference == discovered) {
494 // Reset terminating self-loop to null
495 reference_set_discovered<T>(reference, oop(nullptr));
496 break;
497 }
498 }
499
500 // Prepend discovered references to internal pending list
501 // set_oop_field maintains the card mark barrier as this list is constructed.
502 if (!CompressedOops::is_null(*list)) {
503 oop head = lrb(CompressedOops::decode_not_null(*list));
504 shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier);
505 oop prev = Atomic::xchg(&_pending_list, head);
506 set_oop_field(p, prev);
507 if (prev == nullptr) {
508 // First to prepend to list, record tail
509 _pending_list_tail = reinterpret_cast<void*>(p);
510 }
511
512 // Clear discovered list
513 set_oop_field(list, oop(nullptr));
514 }
515 }
516
517 void ShenandoahReferenceProcessor::work() {
518 // Process discovered references
519 uint max_workers = ShenandoahHeap::heap()->max_workers();
520 uint worker_id = Atomic::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1;
521 while (worker_id < max_workers) {
522 if (UseCompressedOops) {
523 process_references<narrowOop>(_ref_proc_thread_locals[worker_id], worker_id);
524 } else {
525 process_references<oop>(_ref_proc_thread_locals[worker_id], worker_id);
526 }
558 void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent) {
559
560 Atomic::release_store_fence(&_iterate_discovered_list_id, 0U);
561
562 // Process discovered lists
563 ShenandoahReferenceProcessorTask task(phase, concurrent, this);
564 workers->run_task(&task);
565
566 // Update SoftReference clock
567 soft_reference_update_clock();
568
569 // Collect, log and trace statistics
570 collect_statistics();
571
572 enqueue_references(concurrent);
573 }
574
575 void ShenandoahReferenceProcessor::enqueue_references_locked() {
576 // Prepend internal pending list to external pending list
577 shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier);
578
579 // During reference processing, we maintain a local list of references that are identified by
580 // _pending_list and _pending_list_tail. _pending_list_tail points to the next field of the last Reference object on
581 // the local list.
582 //
583 // There is also a global list of reference identified by Universe::_reference_pending_list
584
585 // The following code has the effect of:
586 // 1. Making the global Universe::_reference_pending_list point to my local list
587 // 2. Overwriting the next field of the last Reference on my local list to point at the previous head of the
588 // global Universe::_reference_pending_list
589
590 oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list);
591 if (UseCompressedOops) {
592 set_oop_field<narrowOop>(reinterpret_cast<narrowOop*>(_pending_list_tail), former_head_of_global_list);
593 } else {
594 set_oop_field<oop>(reinterpret_cast<oop*>(_pending_list_tail), former_head_of_global_list);
595 }
596 }
597
598 void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) {
599 if (_pending_list == nullptr) {
600 // Nothing to enqueue
601 return;
602 }
603 if (!concurrent) {
604 // When called from mark-compact or degen-GC, the locking is done by the VMOperation,
605 enqueue_references_locked();
606 } else {
607 // Heap_lock protects external pending list
608 MonitorLocker ml(Heap_lock);
609
610 enqueue_references_locked();
611
612 // Notify ReferenceHandler thread
613 ml.notify_all();
614 }
615
616 // Reset internal pending list
617 _pending_list = nullptr;
618 _pending_list_tail = &_pending_list;
619 }
620
621 template<typename T>
622 void ShenandoahReferenceProcessor::clean_discovered_list(T* list) {
660 for (uint i = 0; i < max_workers; i++) {
661 for (size_t type = 0; type < reference_type_count; type++) {
662 encountered[type] += _ref_proc_thread_locals[i].encountered((ReferenceType)type);
663 discovered[type] += _ref_proc_thread_locals[i].discovered((ReferenceType)type);
664 enqueued[type] += _ref_proc_thread_locals[i].enqueued((ReferenceType)type);
665 }
666 }
667
668 _stats = ReferenceProcessorStats(discovered[REF_SOFT],
669 discovered[REF_WEAK],
670 discovered[REF_FINAL],
671 discovered[REF_PHANTOM]);
672
673 log_info(gc,ref)("Encountered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
674 encountered[REF_SOFT], encountered[REF_WEAK], encountered[REF_FINAL], encountered[REF_PHANTOM]);
675 log_info(gc,ref)("Discovered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
676 discovered[REF_SOFT], discovered[REF_WEAK], discovered[REF_FINAL], discovered[REF_PHANTOM]);
677 log_info(gc,ref)("Enqueued references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT,
678 enqueued[REF_SOFT], enqueued[REF_WEAK], enqueued[REF_FINAL], enqueued[REF_PHANTOM]);
679 }
|