1 /*
  2  * Copyright (c) 2010, 2024, 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_PSCOMPACTIONMANAGER_INLINE_HPP
 26 #define SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_INLINE_HPP
 27 
 28 #include "gc/parallel/psCompactionManager.hpp"
 29 
 30 #include "classfile/classLoaderData.hpp"
 31 #include "classfile/javaClasses.inline.hpp"
 32 #include "gc/parallel/parMarkBitMap.hpp"
 33 #include "gc/parallel/psParallelCompact.inline.hpp"
 34 #include "gc/parallel/psStringDedup.hpp"
 35 #include "gc/shared/partialArrayState.hpp"
 36 #include "gc/shared/partialArrayTaskStepper.inline.hpp"
 37 #include "gc/shared/taskqueue.inline.hpp"
 38 #include "oops/access.inline.hpp"
 39 #include "oops/arrayOop.hpp"
 40 #include "oops/compressedOops.inline.hpp"
 41 #include "oops/objArrayOop.inline.hpp"
 42 #include "oops/oop.inline.hpp"
 43 #include "utilities/debug.hpp"
 44 #include "utilities/globalDefinitions.hpp"
 45 
 46 template <typename T>
 47 inline void PCMarkAndPushClosure::do_oop_work(T* p) {
 48   _compaction_manager->mark_and_push(p);
 49 }
 50 
 51 inline bool ParCompactionManager::steal(int queue_num, ScannerTask& t) {
 52   return marking_stacks()->steal(queue_num, t);
 53 }
 54 
 55 inline bool ParCompactionManager::steal(int queue_num, size_t& region) {
 56   return region_task_queues()->steal(queue_num, region);
 57 }
 58 
 59 inline void ParCompactionManager::push(oop obj) {
 60   marking_stack()->push(ScannerTask(obj));
 61 }
 62 
 63 inline void ParCompactionManager::push(PartialArrayState* stat) {
 64   marking_stack()->push(ScannerTask(stat));
 65 }
 66 
 67 void ParCompactionManager::push_region(size_t index)
 68 {
 69 #ifdef ASSERT
 70   const ParallelCompactData& sd = PSParallelCompact::summary_data();
 71   ParallelCompactData::RegionData* const region_ptr = sd.region(index);
 72   assert(region_ptr->claimed(), "must be claimed");
 73   assert(region_ptr->_pushed++ == 0, "should only be pushed once");
 74 #endif
 75   region_stack()->push(index);
 76 }
 77 
 78 template <typename T>
 79 inline void ParCompactionManager::mark_and_push(T* p) {
 80   T heap_oop = RawAccess<>::oop_load(p);
 81   if (!CompressedOops::is_null(heap_oop)) {
 82     oop obj = CompressedOops::decode_not_null(heap_oop);
 83     assert(ParallelScavengeHeap::heap()->is_in(obj), "should be in heap");
 84 
 85     if (mark_bitmap()->mark_obj(obj)) {
 86       if (StringDedup::is_enabled() &&
 87           java_lang_String::is_instance(obj) &&
 88           psStringDedup::is_candidate_from_mark(obj)) {
 89         _string_dedup_requests.add(obj);
 90       }
 91 
 92       ContinuationGCSupport::transform_stack_chunk(obj);
 93 
 94       assert(_marking_stats_cache != nullptr, "inv");
 95       _marking_stats_cache->push(obj, obj->size());
 96       push(obj);
 97     }
 98   }
 99 }
100 
101 inline void ParCompactionManager::FollowStackClosure::do_void() {
102   _compaction_manager->follow_marking_stacks();
103   if (_terminator != nullptr) {
104     steal_marking_work(*_terminator, _worker_id);
105   }
106 }
107 
108 template <typename T>
109 inline void follow_array_specialized(objArrayOop obj, size_t start, size_t end, ParCompactionManager* cm) {
110   assert(start <= end, "invariant");
111   T* const base = (T*)obj->base();
112   T* const beg = base + start;
113   T* const chunk_end = base + end;
114 
115   // Push the non-null elements of the next stride on the marking stack.
116   for (T* e = beg; e < chunk_end; e++) {
117     cm->mark_and_push<T>(e);
118   }
119 }
120 
121 inline void ParCompactionManager::follow_array(objArrayOop obj, size_t start, size_t end) {
122   if (UseCompressedOops) {
123     follow_array_specialized<narrowOop>(obj, start, end, this);
124   } else {
125     follow_array_specialized<oop>(obj, start, end, this);
126   }
127 }
128 
129 inline void ParCompactionManager::follow_contents(const ScannerTask& task, bool stolen) {
130   if (task.is_partial_array_state()) {
131     assert(PSParallelCompact::mark_bitmap()->is_marked(task.to_partial_array_state()->source()), "should be marked");
132     process_array_chunk(task.to_partial_array_state(), stolen);
133   } else {
134     oop obj = task.to_oop();
135     assert(PSParallelCompact::mark_bitmap()->is_marked(obj), "should be marked");
136     if (obj->is_objArray()) {
137       push_objArray(obj);
138     } else {
139       obj->oop_iterate(&_mark_and_push_closure);
140     }
141   }
142 }
143 
144 inline void ParCompactionManager::MarkingStatsCache::push(size_t region_id, size_t live_words) {
145   size_t index = (region_id & entry_mask);
146   if (entries[index].region_id == region_id) {
147     // Hit
148     entries[index].live_words += live_words;
149     return;
150   }
151   // Miss
152   if (entries[index].live_words != 0) {
153     evict(index);
154   }
155   entries[index].region_id = region_id;
156   entries[index].live_words = live_words;
157 }
158 
159 inline void ParCompactionManager::MarkingStatsCache::push(oop obj, size_t live_words) {
160   ParallelCompactData& data = PSParallelCompact::summary_data();
161   const size_t region_size = ParallelCompactData::RegionSize;
162 
163   HeapWord* addr = cast_from_oop<HeapWord*>(obj);
164   const size_t start_region_id = data.addr_to_region_idx(addr);
165   const size_t end_region_id = data.addr_to_region_idx(addr + live_words - 1);
166   if (start_region_id == end_region_id) {
167     // Completely inside this region
168     push(start_region_id, live_words);
169     return;
170   }
171 
172   // First region
173   push(start_region_id, region_size - data.region_offset(addr));
174 
175   // Middle regions; bypass cache
176   for (size_t i = start_region_id + 1; i < end_region_id; ++i) {
177     data.region(i)->set_partial_obj_size(region_size);
178     data.region(i)->set_partial_obj_addr(addr);
179   }
180 
181   // Last region; bypass cache
182   const size_t end_offset = data.region_offset(addr + live_words - 1);
183   data.region(end_region_id)->set_partial_obj_size(end_offset + 1);
184   data.region(end_region_id)->set_partial_obj_addr(addr);
185 }
186 
187 inline void ParCompactionManager::MarkingStatsCache::evict(size_t index) {
188   ParallelCompactData& data = PSParallelCompact::summary_data();
189   // flush to global data
190   data.region(entries[index].region_id)->add_live_obj(entries[index].live_words);
191 }
192 
193 inline void ParCompactionManager::MarkingStatsCache::evict_all() {
194   for (size_t i = 0; i < num_entries; ++i) {
195     if (entries[i].live_words != 0) {
196       evict(i);
197       entries[i].live_words = 0;
198     }
199   }
200 }
201 
202 inline void ParCompactionManager::create_marking_stats_cache() {
203   assert(_marking_stats_cache == nullptr, "precondition");
204   _marking_stats_cache = new MarkingStatsCache();
205 }
206 
207 inline void ParCompactionManager::flush_and_destroy_marking_stats_cache() {
208   _marking_stats_cache->evict_all();
209   delete _marking_stats_cache;
210   _marking_stats_cache = nullptr;
211 }
212 #endif // SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_INLINE_HPP