31 #include "gc/z/zHash.inline.hpp" |
31 #include "gc/z/zHash.inline.hpp" |
32 #include "gc/z/zLock.inline.hpp" |
32 #include "gc/z/zLock.inline.hpp" |
33 #include "gc/z/zNMethodAllocator.hpp" |
33 #include "gc/z/zNMethodAllocator.hpp" |
34 #include "gc/z/zNMethodData.hpp" |
34 #include "gc/z/zNMethodData.hpp" |
35 #include "gc/z/zNMethodTable.hpp" |
35 #include "gc/z/zNMethodTable.hpp" |
|
36 #include "gc/z/zNMethodTableEntry.hpp" |
36 #include "gc/z/zNMethodTableIteration.hpp" |
37 #include "gc/z/zNMethodTableIteration.hpp" |
37 #include "gc/z/zOopClosures.inline.hpp" |
38 #include "gc/z/zOopClosures.inline.hpp" |
38 #include "gc/z/zTask.hpp" |
39 #include "gc/z/zTask.hpp" |
39 #include "gc/z/zWorkers.hpp" |
40 #include "gc/z/zWorkers.hpp" |
40 #include "logging/log.hpp" |
41 #include "logging/log.hpp" |
49 size_t ZNMethodTable::_size = 0; |
50 size_t ZNMethodTable::_size = 0; |
50 size_t ZNMethodTable::_nregistered = 0; |
51 size_t ZNMethodTable::_nregistered = 0; |
51 size_t ZNMethodTable::_nunregistered = 0; |
52 size_t ZNMethodTable::_nunregistered = 0; |
52 ZNMethodTableIteration ZNMethodTable::_iteration; |
53 ZNMethodTableIteration ZNMethodTable::_iteration; |
53 |
54 |
54 static ZNMethodData* gc_data(const nmethod* nm) { |
|
55 return nm->gc_data<ZNMethodData>(); |
|
56 } |
|
57 |
|
58 static void set_gc_data(nmethod* nm, ZNMethodData* data) { |
|
59 return nm->set_gc_data<ZNMethodData>(data); |
|
60 } |
|
61 |
|
62 ZNMethodTableEntry* ZNMethodTable::create(size_t size) { |
55 ZNMethodTableEntry* ZNMethodTable::create(size_t size) { |
63 void* const mem = ZNMethodAllocator::allocate(size * sizeof(ZNMethodTableEntry)); |
56 void* const mem = ZNMethodAllocator::allocate(size * sizeof(ZNMethodTableEntry)); |
64 return ::new (mem) ZNMethodTableEntry[size]; |
57 return ::new (mem) ZNMethodTableEntry[size]; |
65 } |
58 } |
66 |
59 |
67 void ZNMethodTable::destroy(ZNMethodTableEntry* table) { |
60 void ZNMethodTable::destroy(ZNMethodTableEntry* table) { |
68 ZNMethodAllocator::free(table); |
61 ZNMethodAllocator::free(table); |
69 } |
|
70 |
|
71 void ZNMethodTable::attach_gc_data(nmethod* nm) { |
|
72 GrowableArray<oop*> immediate_oops; |
|
73 bool non_immediate_oops = false; |
|
74 |
|
75 // Find all oops relocations |
|
76 RelocIterator iter(nm); |
|
77 while (iter.next()) { |
|
78 if (iter.type() != relocInfo::oop_type) { |
|
79 // Not an oop |
|
80 continue; |
|
81 } |
|
82 |
|
83 oop_Relocation* r = iter.oop_reloc(); |
|
84 |
|
85 if (!r->oop_is_immediate()) { |
|
86 // Non-immediate oop found |
|
87 non_immediate_oops = true; |
|
88 continue; |
|
89 } |
|
90 |
|
91 if (r->oop_value() != NULL) { |
|
92 // Non-NULL immediate oop found. NULL oops can safely be |
|
93 // ignored since the method will be re-registered if they |
|
94 // are later patched to be non-NULL. |
|
95 immediate_oops.push(r->oop_addr()); |
|
96 } |
|
97 } |
|
98 |
|
99 // Attach GC data to nmethod |
|
100 ZNMethodData* data = gc_data(nm); |
|
101 if (data == NULL) { |
|
102 data = ZNMethodData::create(nm); |
|
103 set_gc_data(nm, data); |
|
104 } |
|
105 |
|
106 // Attach oops in GC data |
|
107 ZNMethodDataOops* const new_oops = ZNMethodDataOops::create(immediate_oops, non_immediate_oops); |
|
108 ZNMethodDataOops* const old_oops = data->swap_oops(new_oops); |
|
109 ZNMethodDataOops::destroy(old_oops); |
|
110 } |
|
111 |
|
112 void ZNMethodTable::detach_gc_data(nmethod* nm) { |
|
113 // Destroy GC data |
|
114 ZNMethodData::destroy(gc_data(nm)); |
|
115 set_gc_data(nm, NULL); |
|
116 } |
|
117 |
|
118 ZReentrantLock* ZNMethodTable::lock_for_nmethod(nmethod* nm) { |
|
119 ZNMethodData* const data = gc_data(nm); |
|
120 if (data == NULL) { |
|
121 return NULL; |
|
122 } |
|
123 return data->lock(); |
|
124 } |
62 } |
125 |
63 |
126 size_t ZNMethodTable::first_index(const nmethod* nm, size_t size) { |
64 size_t ZNMethodTable::first_index(const nmethod* nm, size_t size) { |
127 assert(is_power_of_2(size), "Invalid size"); |
65 assert(is_power_of_2(size), "Invalid size"); |
128 const size_t mask = size - 1; |
66 const size_t mask = size - 1; |
236 rebuild(_size * 2); |
174 rebuild(_size * 2); |
237 } |
175 } |
238 } |
176 } |
239 } |
177 } |
240 |
178 |
241 void ZNMethodTable::log_register(const nmethod* nm) { |
|
242 LogTarget(Trace, gc, nmethod) log; |
|
243 if (!log.is_enabled()) { |
|
244 return; |
|
245 } |
|
246 |
|
247 const ZNMethodDataOops* const oops = gc_data(nm)->oops(); |
|
248 |
|
249 log.print("Register NMethod: %s.%s (" PTR_FORMAT "), " |
|
250 "Compiler: %s, Oops: %d, ImmediateOops: " SIZE_FORMAT ", NonImmediateOops: %s", |
|
251 nm->method()->method_holder()->external_name(), |
|
252 nm->method()->name()->as_C_string(), |
|
253 p2i(nm), |
|
254 nm->compiler_name(), |
|
255 nm->oops_count() - 1, |
|
256 oops->immediates_count(), |
|
257 oops->has_non_immediates() ? "Yes" : "No"); |
|
258 |
|
259 LogTarget(Trace, gc, nmethod, oops) log_oops; |
|
260 if (!log_oops.is_enabled()) { |
|
261 return; |
|
262 } |
|
263 |
|
264 // Print nmethod oops table |
|
265 { |
|
266 oop* const begin = nm->oops_begin(); |
|
267 oop* const end = nm->oops_end(); |
|
268 for (oop* p = begin; p < end; p++) { |
|
269 log_oops.print(" Oop[" SIZE_FORMAT "] " PTR_FORMAT " (%s)", |
|
270 (p - begin), p2i(*p), (*p)->klass()->external_name()); |
|
271 } |
|
272 } |
|
273 |
|
274 // Print nmethod immediate oops |
|
275 { |
|
276 oop** const begin = oops->immediates_begin(); |
|
277 oop** const end = oops->immediates_end(); |
|
278 for (oop** p = begin; p < end; p++) { |
|
279 log_oops.print(" ImmediateOop[" SIZE_FORMAT "] " PTR_FORMAT " @ " PTR_FORMAT " (%s)", |
|
280 (p - begin), p2i(**p), p2i(*p), (**p)->klass()->external_name()); |
|
281 } |
|
282 } |
|
283 } |
|
284 |
|
285 void ZNMethodTable::log_unregister(const nmethod* nm) { |
|
286 LogTarget(Debug, gc, nmethod) log; |
|
287 if (!log.is_enabled()) { |
|
288 return; |
|
289 } |
|
290 |
|
291 log.print("Unregister NMethod: %s.%s (" PTR_FORMAT ")", |
|
292 nm->method()->method_holder()->external_name(), |
|
293 nm->method()->name()->as_C_string(), |
|
294 p2i(nm)); |
|
295 } |
|
296 |
|
297 size_t ZNMethodTable::registered_nmethods() { |
179 size_t ZNMethodTable::registered_nmethods() { |
298 return _nregistered; |
180 return _nregistered; |
299 } |
181 } |
300 |
182 |
301 size_t ZNMethodTable::unregistered_nmethods() { |
183 size_t ZNMethodTable::unregistered_nmethods() { |
302 return _nunregistered; |
184 return _nunregistered; |
303 } |
185 } |
304 |
186 |
305 void ZNMethodTable::register_nmethod(nmethod* nm) { |
187 void ZNMethodTable::register_nmethod(nmethod* nm) { |
306 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
188 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
307 ResourceMark rm; |
|
308 |
189 |
309 // Grow/Shrink/Prune table if needed |
190 // Grow/Shrink/Prune table if needed |
310 rebuild_if_needed(); |
191 rebuild_if_needed(); |
311 |
|
312 // Create and attach gc data |
|
313 attach_gc_data(nm); |
|
314 |
|
315 log_register(nm); |
|
316 |
192 |
317 // Insert new entry |
193 // Insert new entry |
318 if (register_entry(_table, _size, nm)) { |
194 if (register_entry(_table, _size, nm)) { |
319 // New entry registered. When register_entry() instead returns |
195 // New entry registered. When register_entry() instead returns |
320 // false the nmethod was already in the table so we do not want |
196 // false the nmethod was already in the table so we do not want |
321 // to increase number of registered entries in that case. |
197 // to increase number of registered entries in that case. |
322 _nregistered++; |
198 _nregistered++; |
323 } |
199 } |
324 |
|
325 // Disarm nmethod entry barrier |
|
326 disarm_nmethod(nm); |
|
327 } |
200 } |
328 |
201 |
329 void ZNMethodTable::wait_until_iteration_done() { |
202 void ZNMethodTable::wait_until_iteration_done() { |
330 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
203 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
331 |
204 |
334 } |
207 } |
335 } |
208 } |
336 |
209 |
337 void ZNMethodTable::unregister_nmethod(nmethod* nm) { |
210 void ZNMethodTable::unregister_nmethod(nmethod* nm) { |
338 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
211 assert(CodeCache_lock->owned_by_self(), "Lock must be held"); |
339 |
|
340 if (Thread::current()->is_Code_cache_sweeper_thread()) { |
|
341 // The sweeper must wait for any ongoing iteration to complete |
|
342 // before it can unregister an nmethod. |
|
343 ZNMethodTable::wait_until_iteration_done(); |
|
344 } |
|
345 |
|
346 ResourceMark rm; |
|
347 |
|
348 log_unregister(nm); |
|
349 |
212 |
350 // Remove entry |
213 // Remove entry |
351 unregister_entry(_table, _size, nm); |
214 unregister_entry(_table, _size, nm); |
352 _nunregistered++; |
215 _nunregistered++; |
353 _nregistered--; |
216 _nregistered--; |
354 |
|
355 detach_gc_data(nm); |
|
356 } |
|
357 |
|
358 void ZNMethodTable::disarm_nmethod(nmethod* nm) { |
|
359 BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod(); |
|
360 if (bs != NULL) { |
|
361 bs->disarm(nm); |
|
362 } |
|
363 } |
217 } |
364 |
218 |
365 void ZNMethodTable::nmethods_do_begin() { |
219 void ZNMethodTable::nmethods_do_begin() { |
366 MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); |
220 MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); |
367 |
221 |
383 |
237 |
384 // Notify iteration done |
238 // Notify iteration done |
385 CodeCache_lock->notify_all(); |
239 CodeCache_lock->notify_all(); |
386 } |
240 } |
387 |
241 |
388 void ZNMethodTable::oops_do(nmethod* nm, OopClosure* cl) { |
|
389 // Process oops table |
|
390 { |
|
391 oop* const begin = nm->oops_begin(); |
|
392 oop* const end = nm->oops_end(); |
|
393 for (oop* p = begin; p < end; p++) { |
|
394 if (*p != Universe::non_oop_word()) { |
|
395 cl->do_oop(p); |
|
396 } |
|
397 } |
|
398 } |
|
399 |
|
400 ZNMethodDataOops* const oops = gc_data(nm)->oops(); |
|
401 |
|
402 // Process immediate oops |
|
403 { |
|
404 oop** const begin = oops->immediates_begin(); |
|
405 oop** const end = oops->immediates_end(); |
|
406 for (oop** p = begin; p < end; p++) { |
|
407 if (**p != Universe::non_oop_word()) { |
|
408 cl->do_oop(*p); |
|
409 } |
|
410 } |
|
411 } |
|
412 |
|
413 // Process non-immediate oops |
|
414 if (oops->has_non_immediates()) { |
|
415 nm->fix_oop_relocations(); |
|
416 } |
|
417 } |
|
418 |
|
419 class ZNMethodToOopsDoClosure : public NMethodClosure { |
|
420 private: |
|
421 OopClosure* _cl; |
|
422 |
|
423 public: |
|
424 ZNMethodToOopsDoClosure(OopClosure* cl) : |
|
425 _cl(cl) {} |
|
426 |
|
427 virtual void do_nmethod(nmethod* nm) { |
|
428 ZNMethodTable::oops_do(nm, _cl); |
|
429 } |
|
430 }; |
|
431 |
|
432 void ZNMethodTable::oops_do(OopClosure* cl) { |
|
433 ZNMethodToOopsDoClosure nm_cl(cl); |
|
434 nmethods_do(&nm_cl); |
|
435 } |
|
436 |
|
437 void ZNMethodTable::nmethods_do(NMethodClosure* cl) { |
242 void ZNMethodTable::nmethods_do(NMethodClosure* cl) { |
438 _iteration.nmethods_do(cl); |
243 _iteration.nmethods_do(cl); |
439 } |
244 } |
440 |
|
441 class ZNMethodTableUnlinkClosure : public NMethodClosure { |
|
442 private: |
|
443 bool _unloading_occurred; |
|
444 volatile bool _failed; |
|
445 |
|
446 void set_failed() { |
|
447 Atomic::store(true, &_failed); |
|
448 } |
|
449 |
|
450 public: |
|
451 ZNMethodTableUnlinkClosure(bool unloading_occurred) : |
|
452 _unloading_occurred(unloading_occurred), |
|
453 _failed(false) {} |
|
454 |
|
455 virtual void do_nmethod(nmethod* nm) { |
|
456 if (failed()) { |
|
457 return; |
|
458 } |
|
459 |
|
460 if (!nm->is_alive()) { |
|
461 return; |
|
462 } |
|
463 |
|
464 ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm)); |
|
465 |
|
466 if (nm->is_unloading()) { |
|
467 // Unlinking of the dependencies must happen before the |
|
468 // handshake separating unlink and purge. |
|
469 nm->flush_dependencies(false /* delete_immediately */); |
|
470 |
|
471 // We don't need to take the lock when unlinking nmethods from |
|
472 // the Method, because it is only concurrently unlinked by |
|
473 // the entry barrier, which acquires the per nmethod lock. |
|
474 nm->unlink_from_method(false /* acquire_lock */); |
|
475 return; |
|
476 } |
|
477 |
|
478 // Heal oops and disarm |
|
479 ZNMethodOopClosure cl; |
|
480 ZNMethodTable::oops_do(nm, &cl); |
|
481 ZNMethodTable::disarm_nmethod(nm); |
|
482 |
|
483 // Clear compiled ICs and exception caches |
|
484 if (!nm->unload_nmethod_caches(_unloading_occurred)) { |
|
485 set_failed(); |
|
486 } |
|
487 } |
|
488 |
|
489 bool failed() const { |
|
490 return Atomic::load(&_failed); |
|
491 } |
|
492 }; |
|
493 |
|
494 class ZNMethodTableUnlinkTask : public ZTask { |
|
495 private: |
|
496 ZNMethodTableUnlinkClosure _cl; |
|
497 ICRefillVerifier* _verifier; |
|
498 |
|
499 public: |
|
500 ZNMethodTableUnlinkTask(bool unloading_occurred, ICRefillVerifier* verifier) : |
|
501 ZTask("ZNMethodTableUnlinkTask"), |
|
502 _cl(unloading_occurred), |
|
503 _verifier(verifier) { |
|
504 ZNMethodTable::nmethods_do_begin(); |
|
505 } |
|
506 |
|
507 ~ZNMethodTableUnlinkTask() { |
|
508 ZNMethodTable::nmethods_do_end(); |
|
509 } |
|
510 |
|
511 virtual void work() { |
|
512 ICRefillVerifierMark mark(_verifier); |
|
513 ZNMethodTable::nmethods_do(&_cl); |
|
514 } |
|
515 |
|
516 bool success() const { |
|
517 return !_cl.failed(); |
|
518 } |
|
519 }; |
|
520 |
|
521 void ZNMethodTable::unlink(ZWorkers* workers, bool unloading_occurred) { |
|
522 for (;;) { |
|
523 ICRefillVerifier verifier; |
|
524 |
|
525 { |
|
526 ZNMethodTableUnlinkTask task(unloading_occurred, &verifier); |
|
527 workers->run_concurrent(&task); |
|
528 if (task.success()) { |
|
529 return; |
|
530 } |
|
531 } |
|
532 |
|
533 // Cleaning failed because we ran out of transitional IC stubs, |
|
534 // so we have to refill and try again. Refilling requires taking |
|
535 // a safepoint, so we temporarily leave the suspendible thread set. |
|
536 SuspendibleThreadSetLeaver sts; |
|
537 InlineCacheBuffer::refill_ic_stubs(); |
|
538 } |
|
539 } |
|
540 |
|
541 class ZNMethodTablePurgeClosure : public NMethodClosure { |
|
542 public: |
|
543 virtual void do_nmethod(nmethod* nm) { |
|
544 if (nm->is_alive() && nm->is_unloading()) { |
|
545 nm->make_unloaded(); |
|
546 } |
|
547 } |
|
548 }; |
|
549 |
|
550 class ZNMethodTablePurgeTask : public ZTask { |
|
551 private: |
|
552 ZNMethodTablePurgeClosure _cl; |
|
553 |
|
554 public: |
|
555 ZNMethodTablePurgeTask() : |
|
556 ZTask("ZNMethodTablePurgeTask"), |
|
557 _cl() { |
|
558 ZNMethodTable::nmethods_do_begin(); |
|
559 } |
|
560 |
|
561 ~ZNMethodTablePurgeTask() { |
|
562 ZNMethodTable::nmethods_do_end(); |
|
563 } |
|
564 |
|
565 virtual void work() { |
|
566 ZNMethodTable::nmethods_do(&_cl); |
|
567 } |
|
568 }; |
|
569 |
|
570 void ZNMethodTable::purge(ZWorkers* workers) { |
|
571 ZNMethodTablePurgeTask task; |
|
572 workers->run_concurrent(&task); |
|
573 } |
|