1 | /* |
2 | * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. |
3 | * |
4 | * This code is free software; you can redistribute it and/or modify it |
5 | * under the terms of the GNU General Public License version 2 only, as |
6 | * published by the Free Software Foundation. |
7 | * |
8 | * This code is distributed in the hope that it will be useful, but WITHOUT |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
11 | * version 2 for more details (a copy is included in the LICENSE file that |
12 | * accompanied this code). |
13 | * |
14 | * You should have received a copy of the GNU General Public License version |
15 | * 2 along with this work; if not, write to the Free Software Foundation, |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
17 | * |
18 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
19 | * or visit www.oracle.com if you need additional information or have any |
20 | * questions. |
21 | * |
22 | */ |
23 | |
24 | #include "precompiled.hpp" |
25 | #include "code/codeCache.hpp" |
26 | #include "code/nmethod.hpp" |
27 | #include "gc/shenandoah/shenandoahHeap.inline.hpp" |
28 | #include "gc/shenandoah/shenandoahCodeRoots.hpp" |
29 | #include "gc/shenandoah/shenandoahUtils.hpp" |
30 | #include "memory/resourceArea.hpp" |
31 | #include "memory/universe.hpp" |
32 | |
33 | ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray<CodeHeap*>* heaps) { |
34 | _length = heaps->length(); |
35 | _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); |
36 | for (int h = 0; h < _length; h++) { |
37 | _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); |
38 | } |
39 | } |
40 | |
41 | ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { |
42 | FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); |
43 | } |
44 | |
45 | void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { |
46 | for (int c = 0; c < _length; c++) { |
47 | _iters[c].parallel_blobs_do(f); |
48 | } |
49 | } |
50 | |
51 | ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : |
52 | _heap(heap), _claimed_idx(0), _finished(false) { |
53 | } |
54 | |
55 | void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { |
56 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
57 | |
58 | /* |
59 | * Parallel code heap walk. |
60 | * |
61 | * This code makes all threads scan all code heaps, but only one thread would execute the |
62 | * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread |
63 | * had claimed the block, it can process all blobs in it. Others have to fast-forward to |
64 | * next attempt without processing. |
65 | * |
66 | * Late threads would return immediately if iterator is finished. |
67 | */ |
68 | |
69 | if (_finished) { |
70 | return; |
71 | } |
72 | |
73 | int stride = 256; // educated guess |
74 | int stride_mask = stride - 1; |
75 | assert (is_power_of_2(stride), "sanity" ); |
76 | |
77 | int count = 0; |
78 | bool process_block = true; |
79 | |
80 | for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) { |
81 | int current = count++; |
82 | if ((current & stride_mask) == 0) { |
83 | process_block = (current >= _claimed_idx) && |
84 | (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current); |
85 | } |
86 | if (process_block) { |
87 | if (cb->is_alive()) { |
88 | f->do_code_blob(cb); |
89 | #ifdef ASSERT |
90 | if (cb->is_nmethod()) |
91 | Universe::heap()->verify_nmethod((nmethod*)cb); |
92 | #endif |
93 | } |
94 | } |
95 | } |
96 | |
97 | _finished = true; |
98 | } |
99 | |
100 | class ShenandoahNMethodOopDetector : public OopClosure { |
101 | private: |
102 | ResourceMark rm; // For growable array allocation below. |
103 | GrowableArray<oop*> _oops; |
104 | |
105 | public: |
106 | ShenandoahNMethodOopDetector() : _oops(10) {}; |
107 | |
108 | void do_oop(oop* o) { |
109 | _oops.append(o); |
110 | } |
111 | void do_oop(narrowOop* o) { |
112 | fatal("NMethods should not have compressed oops embedded." ); |
113 | } |
114 | |
115 | GrowableArray<oop*>* oops() { |
116 | return &_oops; |
117 | } |
118 | |
119 | bool has_oops() { |
120 | return !_oops.is_empty(); |
121 | } |
122 | }; |
123 | |
124 | GrowableArray<ShenandoahNMethod*>* ShenandoahCodeRoots::_recorded_nms; |
125 | ShenandoahLock ShenandoahCodeRoots::_recorded_nms_lock; |
126 | |
127 | void ShenandoahCodeRoots::initialize() { |
128 | _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray<ShenandoahNMethod*>(100, true, mtGC); |
129 | } |
130 | |
131 | void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { |
132 | switch (ShenandoahCodeRootsStyle) { |
133 | case 0: |
134 | case 1: |
135 | break; |
136 | case 2: { |
137 | assert_locked_or_safepoint(CodeCache_lock); |
138 | ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); |
139 | |
140 | ShenandoahNMethodOopDetector detector; |
141 | nm->oops_do(&detector); |
142 | |
143 | if (detector.has_oops()) { |
144 | ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); |
145 | nmr->assert_alive_and_correct(); |
146 | int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); |
147 | if (idx != -1) { |
148 | ShenandoahNMethod* old = _recorded_nms->at(idx); |
149 | _recorded_nms->at_put(idx, nmr); |
150 | delete old; |
151 | } else { |
152 | _recorded_nms->append(nmr); |
153 | } |
154 | } |
155 | break; |
156 | } |
157 | default: |
158 | ShouldNotReachHere(); |
159 | } |
160 | }; |
161 | |
162 | void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { |
163 | switch (ShenandoahCodeRootsStyle) { |
164 | case 0: |
165 | case 1: { |
166 | break; |
167 | } |
168 | case 2: { |
169 | assert_locked_or_safepoint(CodeCache_lock); |
170 | ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); |
171 | |
172 | ShenandoahNMethodOopDetector detector; |
173 | nm->oops_do(&detector, /* allow_dead = */ true); |
174 | |
175 | if (detector.has_oops()) { |
176 | int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); |
177 | assert(idx != -1, "nmethod " PTR_FORMAT " should be registered" , p2i(nm)); |
178 | ShenandoahNMethod* old = _recorded_nms->at(idx); |
179 | old->assert_same_oops(detector.oops()); |
180 | _recorded_nms->delete_at(idx); |
181 | delete old; |
182 | } |
183 | break; |
184 | } |
185 | default: |
186 | ShouldNotReachHere(); |
187 | } |
188 | } |
189 | |
190 | ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : |
191 | _heap(ShenandoahHeap::heap()), |
192 | _par_iterator(CodeCache::heaps()), |
193 | _claimed(0) { |
194 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
195 | assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers" ); |
196 | switch (ShenandoahCodeRootsStyle) { |
197 | case 0: |
198 | case 1: { |
199 | // No need to do anything here |
200 | break; |
201 | } |
202 | case 2: { |
203 | CodeCache_lock->lock(); |
204 | break; |
205 | } |
206 | default: |
207 | ShouldNotReachHere(); |
208 | } |
209 | } |
210 | |
211 | ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { |
212 | switch (ShenandoahCodeRootsStyle) { |
213 | case 0: |
214 | case 1: { |
215 | // No need to do anything here |
216 | break; |
217 | } |
218 | case 2: { |
219 | CodeCache_lock->unlock(); |
220 | break; |
221 | } |
222 | default: |
223 | ShouldNotReachHere(); |
224 | } |
225 | } |
226 | |
227 | template<bool CSET_FILTER> |
228 | void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { |
229 | switch (ShenandoahCodeRootsStyle) { |
230 | case 0: { |
231 | if (_seq_claimed.try_set()) { |
232 | CodeCache::blobs_do(f); |
233 | } |
234 | break; |
235 | } |
236 | case 1: { |
237 | _par_iterator.parallel_blobs_do(f); |
238 | break; |
239 | } |
240 | case 2: { |
241 | ShenandoahCodeRootsIterator::fast_parallel_blobs_do<CSET_FILTER>(f); |
242 | break; |
243 | } |
244 | default: |
245 | ShouldNotReachHere(); |
246 | } |
247 | } |
248 | |
249 | void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { |
250 | ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<false>(f); |
251 | } |
252 | |
253 | void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { |
254 | ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<true>(f); |
255 | } |
256 | |
257 | template <bool CSET_FILTER> |
258 | void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { |
259 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
260 | |
261 | size_t stride = 256; // educated guess |
262 | |
263 | GrowableArray<ShenandoahNMethod*>* list = ShenandoahCodeRoots::_recorded_nms; |
264 | |
265 | size_t max = (size_t)list->length(); |
266 | while (_claimed < max) { |
267 | size_t cur = Atomic::add(stride, &_claimed) - stride; |
268 | size_t start = cur; |
269 | size_t end = MIN2(cur + stride, max); |
270 | if (start >= max) break; |
271 | |
272 | for (size_t idx = start; idx < end; idx++) { |
273 | ShenandoahNMethod* nmr = list->at((int) idx); |
274 | nmr->assert_alive_and_correct(); |
275 | |
276 | if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { |
277 | continue; |
278 | } |
279 | |
280 | f->do_code_blob(nmr->nm()); |
281 | } |
282 | } |
283 | } |
284 | |
285 | ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>* oops) { |
286 | _nm = nm; |
287 | _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); |
288 | _oops_count = oops->length(); |
289 | for (int c = 0; c < _oops_count; c++) { |
290 | _oops[c] = oops->at(c); |
291 | } |
292 | } |
293 | |
294 | ShenandoahNMethod::~ShenandoahNMethod() { |
295 | if (_oops != NULL) { |
296 | FREE_C_HEAP_ARRAY(oop*, _oops); |
297 | } |
298 | } |
299 | |
300 | bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { |
301 | for (int c = 0; c < _oops_count; c++) { |
302 | oop o = RawAccess<>::oop_load(_oops[c]); |
303 | if (heap->in_collection_set(o)) { |
304 | return true; |
305 | } |
306 | } |
307 | return false; |
308 | } |
309 | |
310 | #ifdef ASSERT |
311 | void ShenandoahNMethod::assert_alive_and_correct() { |
312 | assert(_nm->is_alive(), "only alive nmethods here" ); |
313 | assert(_oops_count > 0, "should have filtered nmethods without oops before" ); |
314 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
315 | for (int c = 0; c < _oops_count; c++) { |
316 | oop *loc = _oops[c]; |
317 | assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*" ); |
318 | oop o = RawAccess<>::oop_load(loc); |
319 | shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress()); |
320 | } |
321 | } |
322 | |
323 | void ShenandoahNMethod::assert_same_oops(GrowableArray<oop*>* oops) { |
324 | assert(_oops_count == oops->length(), "should have the same number of oop*" ); |
325 | for (int c = 0; c < _oops_count; c++) { |
326 | assert(_oops[c] == oops->at(c), "should be the same oop*" ); |
327 | } |
328 | } |
329 | #endif |
330 | |