416 _trace_threshold += large_trace_threshold; |
411 _trace_threshold += large_trace_threshold; |
417 } |
412 } |
418 } |
413 } |
419 } |
414 } |
420 |
415 |
421 // memory region for young generation |
|
422 MemRegion JvmtiTagMap::_young_gen; |
|
423 |
|
424 // get the memory region used for the young generation |
|
425 void JvmtiTagMap::get_young_generation() { |
|
426 CollectedHeap* ch = Universe::heap(); |
|
427 switch (ch->kind()) { |
|
428 case (CollectedHeap::GenCollectedHeap): { |
|
429 _young_gen = ((GenCollectedHeap*)ch)->get_gen(0)->reserved(); |
|
430 break; |
|
431 } |
|
432 #ifndef SERIALGC |
|
433 case (CollectedHeap::ParallelScavengeHeap): { |
|
434 _young_gen = ((ParallelScavengeHeap*)ch)->young_gen()->reserved(); |
|
435 break; |
|
436 } |
|
437 case (CollectedHeap::G1CollectedHeap): { |
|
438 // Until a more satisfactory solution is implemented, all |
|
439 // oops in the tag map will require rehash at each gc. |
|
440 // This is a correct, if extremely inefficient solution. |
|
441 // See RFE 6621729 for related commentary. |
|
442 _young_gen = ch->reserved_region(); |
|
443 break; |
|
444 } |
|
445 #endif // !SERIALGC |
|
446 default: |
|
447 ShouldNotReachHere(); |
|
448 } |
|
449 } |
|
450 |
|
451 // returns true if oop is in the young generation |
|
452 inline bool JvmtiTagMap::is_in_young(oop o) { |
|
453 assert(_young_gen.start() != NULL, "checking"); |
|
454 void* p = (void*)o; |
|
455 bool in_young = _young_gen.contains(p); |
|
456 return in_young; |
|
457 } |
|
458 |
|
459 // returns the appropriate hashmap for a given object |
|
460 inline JvmtiTagHashmap* JvmtiTagMap::hashmap_for(oop o) { |
|
461 if (is_in_young(o)) { |
|
462 return _hashmap[0]; |
|
463 } else { |
|
464 return _hashmap[1]; |
|
465 } |
|
466 } |
|
467 |
|
468 |
|
469 // create a JvmtiTagMap |
416 // create a JvmtiTagMap |
470 JvmtiTagMap::JvmtiTagMap(JvmtiEnv* env) : |
417 JvmtiTagMap::JvmtiTagMap(JvmtiEnv* env) : |
471 _env(env), |
418 _env(env), |
472 _lock(Mutex::nonleaf+2, "JvmtiTagMap._lock", false), |
419 _lock(Mutex::nonleaf+2, "JvmtiTagMap._lock", false), |
473 _free_entries(NULL), |
420 _free_entries(NULL), |
474 _free_entries_count(0) |
421 _free_entries_count(0) |
475 { |
422 { |
476 assert(JvmtiThreadState_lock->is_locked(), "sanity check"); |
423 assert(JvmtiThreadState_lock->is_locked(), "sanity check"); |
477 assert(((JvmtiEnvBase *)env)->tag_map() == NULL, "tag map already exists for environment"); |
424 assert(((JvmtiEnvBase *)env)->tag_map() == NULL, "tag map already exists for environment"); |
478 |
425 |
479 // create the hashmaps |
426 _hashmap = new JvmtiTagHashmap(); |
480 for (int i=0; i<n_hashmaps; i++) { |
|
481 _hashmap[i] = new JvmtiTagHashmap(); |
|
482 } |
|
483 |
|
484 // get the memory region used by the young generation |
|
485 get_young_generation(); |
|
486 |
427 |
487 // finally add us to the environment |
428 // finally add us to the environment |
488 ((JvmtiEnvBase *)env)->set_tag_map(this); |
429 ((JvmtiEnvBase *)env)->set_tag_map(this); |
489 } |
430 } |
490 |
431 |
494 |
435 |
495 // no lock acquired as we assume the enclosing environment is |
436 // no lock acquired as we assume the enclosing environment is |
496 // also being destroryed. |
437 // also being destroryed. |
497 ((JvmtiEnvBase *)_env)->set_tag_map(NULL); |
438 ((JvmtiEnvBase *)_env)->set_tag_map(NULL); |
498 |
439 |
499 // iterate over the hashmaps and destroy each of the entries |
440 JvmtiTagHashmapEntry** table = _hashmap->table(); |
500 for (int i=0; i<n_hashmaps; i++) { |
441 for (int j = 0; j < _hashmap->size(); j++) { |
501 JvmtiTagHashmap* hashmap = _hashmap[i]; |
442 JvmtiTagHashmapEntry* entry = table[j]; |
502 JvmtiTagHashmapEntry** table = hashmap->table(); |
443 while (entry != NULL) { |
503 for (int j=0; j<hashmap->size(); j++) { |
444 JvmtiTagHashmapEntry* next = entry->next(); |
504 JvmtiTagHashmapEntry *entry = table[j]; |
445 delete entry; |
505 while (entry != NULL) { |
446 entry = next; |
506 JvmtiTagHashmapEntry* next = entry->next(); |
447 } |
507 jweak ref = entry->object(); |
448 } |
508 JNIHandles::destroy_weak_global(ref); |
449 |
509 delete entry; |
450 // finally destroy the hashmap |
510 entry = next; |
451 delete _hashmap; |
511 } |
452 _hashmap = NULL; |
512 } |
|
513 |
|
514 // finally destroy the hashmap |
|
515 delete hashmap; |
|
516 } |
|
517 |
453 |
518 // remove any entries on the free list |
454 // remove any entries on the free list |
519 JvmtiTagHashmapEntry* entry = _free_entries; |
455 JvmtiTagHashmapEntry* entry = _free_entries; |
520 while (entry != NULL) { |
456 while (entry != NULL) { |
521 JvmtiTagHashmapEntry* next = entry->next(); |
457 JvmtiTagHashmapEntry* next = entry->next(); |
522 delete entry; |
458 delete entry; |
523 entry = next; |
459 entry = next; |
524 } |
460 } |
|
461 _free_entries = NULL; |
525 } |
462 } |
526 |
463 |
527 // create a hashmap entry |
464 // create a hashmap entry |
528 // - if there's an entry on the (per-environment) free list then this |
465 // - if there's an entry on the (per-environment) free list then this |
529 // is returned. Otherwise an new entry is allocated. |
466 // is returned. Otherwise an new entry is allocated. |
530 JvmtiTagHashmapEntry* JvmtiTagMap::create_entry(jweak ref, jlong tag) { |
467 JvmtiTagHashmapEntry* JvmtiTagMap::create_entry(oop ref, jlong tag) { |
531 assert(Thread::current()->is_VM_thread() || is_locked(), "checking"); |
468 assert(Thread::current()->is_VM_thread() || is_locked(), "checking"); |
532 JvmtiTagHashmapEntry* entry; |
469 JvmtiTagHashmapEntry* entry; |
533 if (_free_entries == NULL) { |
470 if (_free_entries == NULL) { |
534 entry = new JvmtiTagHashmapEntry(ref, tag); |
471 entry = new JvmtiTagHashmapEntry(ref, tag); |
535 } else { |
472 } else { |
794 |
722 |
795 // tag an object |
723 // tag an object |
796 // |
724 // |
797 // This function is performance critical. If many threads attempt to tag objects |
725 // This function is performance critical. If many threads attempt to tag objects |
798 // around the same time then it's possible that the Mutex associated with the |
726 // around the same time then it's possible that the Mutex associated with the |
799 // tag map will be a hot lock. Eliminating this lock will not eliminate the issue |
727 // tag map will be a hot lock. |
800 // because creating a JNI weak reference requires acquiring a global lock also. |
|
801 void JvmtiTagMap::set_tag(jobject object, jlong tag) { |
728 void JvmtiTagMap::set_tag(jobject object, jlong tag) { |
802 MutexLocker ml(lock()); |
729 MutexLocker ml(lock()); |
803 |
730 |
804 // resolve the object |
731 // resolve the object |
805 oop o = JNIHandles::resolve_non_null(object); |
732 oop o = JNIHandles::resolve_non_null(object); |
806 |
733 |
807 // for Classes we tag the klassOop |
734 // for Classes we tag the klassOop |
808 o = klassOop_if_java_lang_Class(o); |
735 o = klassOop_if_java_lang_Class(o); |
809 |
736 |
810 // see if the object is already tagged |
737 // see if the object is already tagged |
811 JvmtiTagHashmap* hashmap = hashmap_for(o); |
738 JvmtiTagHashmap* hashmap = _hashmap; |
812 JvmtiTagHashmapEntry* entry = hashmap->find(o); |
739 JvmtiTagHashmapEntry* entry = hashmap->find(o); |
813 |
740 |
814 // if the object is not already tagged then we tag it |
741 // if the object is not already tagged then we tag it |
815 if (entry == NULL) { |
742 if (entry == NULL) { |
816 if (tag != 0) { |
743 if (tag != 0) { |
817 HandleMark hm; |
744 entry = create_entry(o, tag); |
818 Handle h(o); |
745 hashmap->add(o, entry); |
819 jweak ref = JNIHandles::make_weak_global(h); |
|
820 |
|
821 // the object may have moved because make_weak_global may |
|
822 // have blocked - thus it is necessary resolve the handle |
|
823 // and re-hash the object. |
|
824 o = h(); |
|
825 entry = create_entry(ref, tag); |
|
826 hashmap_for(o)->add(o, entry); |
|
827 } else { |
746 } else { |
828 // no-op |
747 // no-op |
829 } |
748 } |
830 } else { |
749 } else { |
831 // if the object is already tagged then we either update |
750 // if the object is already tagged then we either update |
832 // the tag (if a new tag value has been provided) |
751 // the tag (if a new tag value has been provided) |
833 // or remove the object if the new tag value is 0. |
752 // or remove the object if the new tag value is 0. |
834 // Removing the object requires that we also delete the JNI |
|
835 // weak ref to the object. |
|
836 if (tag == 0) { |
753 if (tag == 0) { |
837 jweak ref = entry->object(); |
|
838 hashmap->remove(o); |
754 hashmap->remove(o); |
839 destroy_entry(entry); |
755 destroy_entry(entry); |
840 JNIHandles::destroy_weak_global(ref); |
|
841 } else { |
756 } else { |
842 entry->set_tag(tag); |
757 entry->set_tag(tag); |
843 } |
758 } |
844 } |
759 } |
845 } |
760 } |
3372 VM_HeapWalkOperation op(this, initial_object, context, user_data); |
3287 VM_HeapWalkOperation op(this, initial_object, context, user_data); |
3373 VMThread::execute(&op); |
3288 VMThread::execute(&op); |
3374 } |
3289 } |
3375 |
3290 |
3376 |
3291 |
3377 // called post-GC |
3292 void JvmtiTagMap::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { |
3378 // - for each JVMTI environment with an object tag map, call its rehash |
3293 assert(SafepointSynchronize::is_at_safepoint(), |
3379 // function to re-sync with the new object locations. |
3294 "must be executed at a safepoint"); |
3380 void JvmtiTagMap::gc_epilogue(bool full) { |
|
3381 assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); |
|
3382 if (JvmtiEnv::environments_might_exist()) { |
3295 if (JvmtiEnv::environments_might_exist()) { |
3383 // re-obtain the memory region for the young generation (might |
|
3384 // changed due to adaptive resizing policy) |
|
3385 get_young_generation(); |
|
3386 |
|
3387 JvmtiEnvIterator it; |
3296 JvmtiEnvIterator it; |
3388 for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { |
3297 for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { |
3389 JvmtiTagMap* tag_map = env->tag_map(); |
3298 JvmtiTagMap* tag_map = env->tag_map(); |
3390 if (tag_map != NULL && !tag_map->is_empty()) { |
3299 if (tag_map != NULL && !tag_map->is_empty()) { |
3391 TraceTime t(full ? "JVMTI Full Rehash " : "JVMTI Rehash ", TraceJVMTIObjectTagging); |
3300 tag_map->do_weak_oops(is_alive, f); |
3392 if (full) { |
|
3393 tag_map->rehash(0, n_hashmaps); |
|
3394 } else { |
|
3395 tag_map->rehash(0, 0); // tag map for young gen only |
|
3396 } |
|
3397 } |
3301 } |
3398 } |
3302 } |
3399 } |
3303 } |
3400 } |
3304 } |
3401 |
3305 |
3402 // CMS has completed referencing processing so we may have JNI weak refs |
3306 void JvmtiTagMap::do_weak_oops(BoolObjectClosure* is_alive, OopClosure* f) { |
3403 // to objects in the CMS generation that have been GC'ed. |
|
3404 void JvmtiTagMap::cms_ref_processing_epilogue() { |
|
3405 assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); |
|
3406 assert(UseConcMarkSweepGC, "should only be used with CMS"); |
|
3407 if (JvmtiEnv::environments_might_exist()) { |
|
3408 JvmtiEnvIterator it; |
|
3409 for (JvmtiEnvBase* env = it.first(); env != NULL; env = it.next(env)) { |
|
3410 JvmtiTagMap* tag_map = ((JvmtiEnvBase *)env)->tag_map(); |
|
3411 if (tag_map != NULL && !tag_map->is_empty()) { |
|
3412 TraceTime t("JVMTI Rehash (CMS) ", TraceJVMTIObjectTagging); |
|
3413 tag_map->rehash(1, n_hashmaps); // assume CMS not used in young gen |
|
3414 } |
|
3415 } |
|
3416 } |
|
3417 } |
|
3418 |
|
3419 |
|
3420 // For each entry in the hashmaps 'start' to 'end' : |
|
3421 // |
|
3422 // 1. resolve the JNI weak reference |
|
3423 // |
|
3424 // 2. If it resolves to NULL it means the object has been freed so the entry |
|
3425 // is removed, the weak reference destroyed, and the object free event is |
|
3426 // posted (if enabled). |
|
3427 // |
|
3428 // 3. If the weak reference resolves to an object then we re-hash the object |
|
3429 // to see if it has moved or has been promoted (from the young to the old |
|
3430 // generation for example). |
|
3431 // |
|
3432 void JvmtiTagMap::rehash(int start, int end) { |
|
3433 |
3307 |
3434 // does this environment have the OBJECT_FREE event enabled |
3308 // does this environment have the OBJECT_FREE event enabled |
3435 bool post_object_free = env()->is_enabled(JVMTI_EVENT_OBJECT_FREE); |
3309 bool post_object_free = env()->is_enabled(JVMTI_EVENT_OBJECT_FREE); |
3436 |
3310 |
3437 // counters used for trace message |
3311 // counters used for trace message |
3438 int freed = 0; |
3312 int freed = 0; |
3439 int moved = 0; |
3313 int moved = 0; |
3440 int promoted = 0; |
3314 |
3441 |
3315 JvmtiTagHashmap* hashmap = this->hashmap(); |
3442 // we assume there are two hashmaps - one for the young generation |
|
3443 // and the other for all other spaces. |
|
3444 assert(n_hashmaps == 2, "not implemented"); |
|
3445 JvmtiTagHashmap* young_hashmap = _hashmap[0]; |
|
3446 JvmtiTagHashmap* other_hashmap = _hashmap[1]; |
|
3447 |
3316 |
3448 // reenable sizing (if disabled) |
3317 // reenable sizing (if disabled) |
3449 young_hashmap->set_resizing_enabled(true); |
3318 hashmap->set_resizing_enabled(true); |
3450 other_hashmap->set_resizing_enabled(true); |
3319 |
3451 |
3320 // if the hashmap is empty then we can skip it |
3452 // when re-hashing the hashmap corresponding to the young generation we |
3321 if (hashmap->_entry_count == 0) { |
3453 // collect the entries corresponding to objects that have been promoted. |
3322 return; |
3454 JvmtiTagHashmapEntry* promoted_entries = NULL; |
3323 } |
3455 |
3324 |
3456 if (end >= n_hashmaps) { |
3325 // now iterate through each entry in the table |
3457 end = n_hashmaps - 1; |
3326 |
3458 } |
3327 JvmtiTagHashmapEntry** table = hashmap->table(); |
3459 |
3328 int size = hashmap->size(); |
3460 for (int i=start; i <= end; i++) { |
3329 |
3461 JvmtiTagHashmap* hashmap = _hashmap[i]; |
3330 JvmtiTagHashmapEntry* delayed_add = NULL; |
3462 |
3331 |
3463 // if the hashmap is empty then we can skip it |
3332 for (int pos = 0; pos < size; ++pos) { |
3464 if (hashmap->_entry_count == 0) { |
3333 JvmtiTagHashmapEntry* entry = table[pos]; |
3465 continue; |
3334 JvmtiTagHashmapEntry* prev = NULL; |
3466 } |
3335 |
3467 |
3336 while (entry != NULL) { |
3468 // now iterate through each entry in the table |
3337 JvmtiTagHashmapEntry* next = entry->next(); |
3469 |
3338 |
3470 JvmtiTagHashmapEntry** table = hashmap->table(); |
3339 oop* obj = entry->object_addr(); |
3471 int size = hashmap->size(); |
3340 |
3472 |
3341 // has object been GC'ed |
3473 for (int pos=0; pos<size; pos++) { |
3342 if (!is_alive->do_object_b(entry->object())) { |
3474 JvmtiTagHashmapEntry* entry = table[pos]; |
3343 // grab the tag |
3475 JvmtiTagHashmapEntry* prev = NULL; |
3344 jlong tag = entry->tag(); |
3476 |
3345 guarantee(tag != 0, "checking"); |
3477 while (entry != NULL) { |
3346 |
3478 JvmtiTagHashmapEntry* next = entry->next(); |
3347 // remove GC'ed entry from hashmap and return the |
3479 |
3348 // entry to the free list |
3480 jweak ref = entry->object(); |
3349 hashmap->remove(prev, pos, entry); |
3481 oop oop = JNIHandles::resolve(ref); |
3350 destroy_entry(entry); |
3482 |
3351 |
3483 // has object been GC'ed |
3352 // post the event to the profiler |
3484 if (oop == NULL) { |
3353 if (post_object_free) { |
3485 // grab the tag |
3354 JvmtiExport::post_object_free(env(), tag); |
3486 jlong tag = entry->tag(); |
3355 } |
3487 guarantee(tag != 0, "checking"); |
3356 |
3488 |
3357 ++freed; |
3489 // remove GC'ed entry from hashmap and return the |
3358 } else { |
3490 // entry to the free list |
3359 f->do_oop(entry->object_addr()); |
3491 hashmap->remove(prev, pos, entry); |
3360 oop new_oop = entry->object(); |
3492 destroy_entry(entry); |
3361 |
3493 |
3362 // if the object has moved then re-hash it and move its |
3494 // destroy the weak ref |
3363 // entry to its new location. |
3495 JNIHandles::destroy_weak_global(ref); |
3364 unsigned int new_pos = JvmtiTagHashmap::hash(new_oop, size); |
3496 |
3365 if (new_pos != (unsigned int)pos) { |
3497 // post the event to the profiler |
3366 if (prev == NULL) { |
3498 if (post_object_free) { |
3367 table[pos] = next; |
3499 JvmtiExport::post_object_free(env(), tag); |
3368 } else { |
|
3369 prev->set_next(next); |
3500 } |
3370 } |
3501 |
3371 if (new_pos < (unsigned int)pos) { |
3502 freed++; |
|
3503 entry = next; |
|
3504 continue; |
|
3505 } |
|
3506 |
|
3507 // if this is the young hashmap then the object is either promoted |
|
3508 // or moved. |
|
3509 // if this is the other hashmap then the object is moved. |
|
3510 |
|
3511 bool same_gen; |
|
3512 if (i == 0) { |
|
3513 assert(hashmap == young_hashmap, "checking"); |
|
3514 same_gen = is_in_young(oop); |
|
3515 } else { |
|
3516 same_gen = true; |
|
3517 } |
|
3518 |
|
3519 |
|
3520 if (same_gen) { |
|
3521 // if the object has moved then re-hash it and move its |
|
3522 // entry to its new location. |
|
3523 unsigned int new_pos = JvmtiTagHashmap::hash(oop, size); |
|
3524 if (new_pos != (unsigned int)pos) { |
|
3525 if (prev == NULL) { |
|
3526 table[pos] = next; |
|
3527 } else { |
|
3528 prev->set_next(next); |
|
3529 } |
|
3530 entry->set_next(table[new_pos]); |
3372 entry->set_next(table[new_pos]); |
3531 table[new_pos] = entry; |
3373 table[new_pos] = entry; |
3532 moved++; |
|
3533 } else { |
3374 } else { |
3534 // object didn't move |
3375 // Delay adding this entry to it's new position as we'd end up |
3535 prev = entry; |
3376 // hitting it again during this iteration. |
|
3377 entry->set_next(delayed_add); |
|
3378 delayed_add = entry; |
3536 } |
3379 } |
|
3380 moved++; |
3537 } else { |
3381 } else { |
3538 // object has been promoted so remove the entry from the |
3382 // object didn't move |
3539 // young hashmap |
3383 prev = entry; |
3540 assert(hashmap == young_hashmap, "checking"); |
|
3541 hashmap->remove(prev, pos, entry); |
|
3542 |
|
3543 // move the entry to the promoted list |
|
3544 entry->set_next(promoted_entries); |
|
3545 promoted_entries = entry; |
|
3546 } |
3384 } |
3547 |
|
3548 entry = next; |
|
3549 } |
3385 } |
3550 } |
3386 |
3551 } |
3387 entry = next; |
3552 |
3388 } |
3553 |
3389 } |
3554 // add the entries, corresponding to the promoted objects, to the |
3390 |
3555 // other hashmap. |
3391 // Re-add all the entries which were kept aside |
3556 JvmtiTagHashmapEntry* entry = promoted_entries; |
3392 while (delayed_add != NULL) { |
3557 while (entry != NULL) { |
3393 JvmtiTagHashmapEntry* next = delayed_add->next(); |
3558 oop o = JNIHandles::resolve(entry->object()); |
3394 unsigned int pos = JvmtiTagHashmap::hash(delayed_add->object(), size); |
3559 assert(hashmap_for(o) == other_hashmap, "checking"); |
3395 delayed_add->set_next(table[pos]); |
3560 JvmtiTagHashmapEntry* next = entry->next(); |
3396 table[pos] = delayed_add; |
3561 other_hashmap->add(o, entry); |
3397 delayed_add = next; |
3562 entry = next; |
|
3563 promoted++; |
|
3564 } |
3398 } |
3565 |
3399 |
3566 // stats |
3400 // stats |
3567 if (TraceJVMTIObjectTagging) { |
3401 if (TraceJVMTIObjectTagging) { |
3568 int total_moves = promoted + moved; |
3402 int post_total = hashmap->_entry_count; |
3569 |
|
3570 int post_total = 0; |
|
3571 for (int i=0; i<n_hashmaps; i++) { |
|
3572 post_total += _hashmap[i]->_entry_count; |
|
3573 } |
|
3574 int pre_total = post_total + freed; |
3403 int pre_total = post_total + freed; |
3575 |
3404 |
3576 tty->print("(%d->%d, %d freed, %d promoted, %d total moves)", |
3405 tty->print_cr("(%d->%d, %d freed, %d total moves)", |
3577 pre_total, post_total, freed, promoted, total_moves); |
3406 pre_total, post_total, freed, moved); |
3578 } |
3407 } |
3579 } |
3408 } |