RFR: 8212879: Make JVMTI TagMap table concurrent [v5]
Coleen Phillimore
coleenp at openjdk.java.net
Wed Nov 4 12:21:12 UTC 2020
On Wed, 4 Nov 2020 05:37:00 GMT, Serguei Spitsyn <sspitsyn at openjdk.org> wrote:
>> Hi Coleen,
>>
>> Wow, there are a lot of simplifications and code removal with this fix!
>> It looks great in general, just some nits below.
>> I also wanted to suggest renaming the 'set_needs_processing' to 'set_needs_rehashing'. :)
>>
>> src/hotspot/share/prims/jvmtiTagMap.hpp:
>>
>> Nit: Would it better to use a plural form 'post_dead_objects_on_vm_thread'? :
>> `+ void post_dead_object_on_vm_thread();`
>>
>> src/hotspot/share/prims/jvmtiTagMap.cpp:
>>
>> Nit: It'd be nice to add a short comment before the check_hashmap similar to L143 also explaining a difference (does check and post just for one env) with the check_hashmaps_for_heapwalk:
>> 122 void JvmtiTagMap::check_hashmap(bool post_events) {
>> . . .
>> 143 // This checks for posting and rehashing and is called from the heap walks.
>> 144 void JvmtiTagMap::check_hashmaps_for_heapwalk() {
>>
>> I'm just curious how this fragment was added. Did you get any failures in testing? :
>> 1038 // skip if object is a dormant shared object whose mirror hasn't been loaded
>> 1039 if (obj != NULL && obj->klass()->java_mirror() == NULL) {
>> 1040 log_debug(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s)", p2i(obj),
>> 1041 obj->klass()->external_name());
>> 1042 return;
>> 1043 }
>>
>> Nit: Can we rename this field to something like '_some_dead_found' or '_dead_found'? :
>> `1186 bool _some_dead;`
>>
>> Nit: The lines 2997-3007 and 3009-3019 do the same but in different contexts.
>> 2996 if (!is_vm_thread) {
>> 2997 if (num_dead_entries != 0) {
>> 2998 JvmtiEnvIterator it;
>> 2999 for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
>> 3000 JvmtiTagMap* tag_map = env->tag_map_acquire();
>> 3001 if (tag_map != NULL) {
>> 3002 // Lock each hashmap from concurrent posting and cleaning
>> 3003 tag_map->unlink_and_post_locked();
>> 3004 }
>> 3005 }
>> 3006 // there's another callback for needs_rehashing
>> 3007 }
>> 3008 } else {
>> 3009 assert(SafepointSynchronize::is_at_safepoint(), "must be");
>> 3010 JvmtiEnvIterator it;
>> 3011 for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
>> 3012 JvmtiTagMap* tag_map = env->tag_map_acquire();
>> 3013 if (tag_map != NULL && !tag_map->is_empty()) {
>> 3014 if (num_dead_entries != 0) {
>> 3015 tag_map->hashmap()->unlink_and_post(tag_map->env());
>> 3016 }
>> 3017 // Later GC code will relocate the oops, so defer rehashing until then.
>> 3018 tag_map->_needs_rehashing = true;
>> 3019 }
>> It feels like it can be refactored/simplified, at least, a little bit.
>> Is it possible to check and just return if (num_dead_entries == 0)?
>> If not, then, at least, it can be done the same way (except of locking).
>> Q: Should the _needs_rehashing be set in both contexts?
>>
>> Also, can we have just one (static?) lock for the whole gc_notification (not per JVMTI env/JvmtiTagMap)? How much do we win by locking per each env/JvmtiTagMap? Note, that in normal case there is just one agent. It is very rare to have multiple agents requesting object tagging and ObjectFree events. It seems, this can be refactored to more simple code with one function doing work in both contexts.
>>
>> src/hotspot/share/utilities/hashtable.cpp:
>>
>> Nit: Need space after the '{' :
>> `+const int _small_table_sizes[] = {107, 1009, 2017, 4049, 5051, 10103, 20201, 40423 } ;`
>>
>> src/hotspot/share/prims/jvmtiTagMapTable.cpp:
>>
>> Nit: Extra space after assert:
>> `119 assert (find(index, hash, obj) == NULL, "shouldn't already be present");`
>>
>> Thanks,
>> Serguei
>
> More about possible refactoring of the JvmtiTagMap::gc_notification().
> I'm thinking about something like below:
>
> void JvmtiTagMap::unlink_and_post_for_all_envs() {
> if (num_dead_entries == 0) {
> return; // nothing to unlink and post
> }
> JvmtiEnvIterator it;
> for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
> JvmtiTagMap* tag_map = env->tag_map_acquire();
> if (tag_map != NULL && !tag_map->is_empty()) {
> tag_map->unlink_and_post();
> }
> }
> }
>
> void JvmtiTagMap::gc_notification(size_t num_dead_entries) {
> if (Thread::current()->is_VM_thread()) {
> assert(SafepointSynchronize::is_at_safepoint(), "must be");
> unlink_and_post_for_all_envs();
> set_needs_rehashing();
> } else {
> MutexLocker ml(JvmtiTagMap_lock(), Mutex::_no_safepoint_check_flag);
> unlink_and_post_for_all_envs();
> // there's another callback for needs_rehashing
> }
> }
>
> If we still need a lock per each JvmtiTagMap then it is possible to add this fragment to the unlink_and_post_for_all_envs:
> bool is_vm_thread = Thread::current()->is_VM_thread()
> MutexLocker ml(is_vm_thread ? NULL : lock(), Mutex::_no_safepoint_check_flag);
>
> Then the code above could look like below:
>
> void JvmtiTagMap::unlink_and_post_for_all_envs() {
> if (num_dead_entries == 0) {
> return; // nothing to unlink and post
> }
> bool is_vm_thread = Thread::current()->is_VM_thread()
> JvmtiEnvIterator it;
> for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
> JvmtiTagMap* tag_map = env->tag_map_acquire();
> if (tag_map != NULL && !tag_map->is_empty()) {
> MutexLocker ml(is_vm_thread ? NULL : lock(), Mutex::_no_safepoint_check_flag);
> tag_map->unlink_and_post();
> }
> }
> }
>
> void JvmtiTagMap::gc_notification(size_t num_dead_entries) {
> if (Thread::current()->is_VM_thread()) {
> assert(SafepointSynchronize::is_at_safepoint(), "must be");
> set_needs_rehashing();
> }
> unlink_and_post_for_all_envs();
> }
@sspitsyn Thank you for reviewing the code. The gc_notification refactoring is awkward because each table needs a lock and not a global lock. If we find another place in the GCs to call set_needs_rehashing() it might be possible to make gc_notification call two functions with a boolean to decide whether to take the lock. We're still working on @kimbarrett comments so maybe the notification will change to some new thread and be refactored that way if necessary. I fixed the code for your other comments.
-------------
PR: https://git.openjdk.java.net/jdk/pull/967
More information about the serviceability-dev
mailing list