On Mon, 2 Nov 2020 08:08:53 GMT, Stefan Karlsson <stef...@openjdk.org> wrote:
>> This change turns the HashTable that JVMTI uses for object tagging into a >> regular Hotspot hashtable - the one in hashtable.hpp with resizing and >> rehashing. Instead of pointing directly to oops so that GC has to walk the >> table to follow oops and then to rehash the table, this table points to >> WeakHandle. GC walks the backing OopStorages concurrently. >> >> The hash function for the table is a hash of the lower 32 bits of the >> address. A flag is set during GC (gc_notification if in a safepoint, and >> through a call to JvmtiTagMap::needs_processing()) so that the table is >> rehashed at the next use. >> >> The gc_notification mechanism of weak oop processing is used to notify Jvmti >> to post ObjectFree events. In concurrent GCs there can be a window of time >> between weak oop marking where the oop is unmarked, so dead (the phantom >> load in peek returns NULL) but the gc_notification hasn't been done yet. In >> this window, a heap walk or GetObjectsWithTags call would not find an object >> before the ObjectFree event is posted. This is dealt with in two ways: >> >> 1. In the Heap walk, there's an unconditional table walk to post events if >> events are needed to post. >> 2. For GetObjectWithTags, if a dead oop is found in the table and posting is >> required, we use the VM thread to post the event. >> >> Event posting cannot be done in a JavaThread because the posting needs to be >> done while holding the table lock, so that the JvmtiEnv state doesn't change >> before posting is done. ObjectFree callbacks are limited in what they can >> do as per the JVMTI Specification. The allowed callbacks to the VM already >> have code to allow NonJava threads. >> >> To avoid rehashing, I also tried to use object->identity_hash() but this >> breaks because entries can be added to the table during heapwalk, where the >> objects use marking. The starting markWord is saved and restored. Adding a >> hashcode during this operation makes restoring the former markWord (locked, >> inflated, etc) too complicated. Plus we don't want all these objects to >> have hashcodes because locking operations after tagging would have to always >> use inflated locks. >> >> Much of this change is to remove serial weak oop processing for the >> weakProcessor, ZGC and Shenandoah. The GCs have been stress tested with >> jvmti code. >> >> It has also been tested with tier1-6. >> >> Thank you to Stefan, Erik and Kim for their help with this change. > > src/hotspot/share/gc/shared/oopStorageSet.hpp line 41: > >> 39: // Must be updated when new OopStorages are introduced >> 40: static const uint strong_count = 4 JVMTI_ONLY(+ 1); >> 41: static const uint weak_count = 5 JVMTI_ONLY(+1) JFR_ONLY(+ 1); > > All other uses `+ 1` instead of `+1`. Fixed, although I think the space looks strange there but I'll go along. > src/hotspot/share/gc/shared/weakProcessorPhaseTimes.hpp line 49: > >> 47: double _phase_times_sec[1]; >> 48: size_t _phase_dead_items[1]; >> 49: size_t _phase_total_items[1]; > > This should be removed and the associated reset_items Removed. > src/hotspot/share/gc/z/zOopClosures.hpp line 64: > >> 62: }; >> 63: >> 64: class ZPhantomKeepAliveOopClosure : public ZRootsIteratorClosure { > > Seems like you flipped the location of these two. Maybe revert? Reverted. There was a rebasing conflict here so this was unintentional. > src/hotspot/share/prims/jvmtiExport.hpp line 405: > >> 403: >> 404: // Delete me and all my callers! >> 405: static void weak_oops_do(BoolObjectClosure* b, OopClosure* f) {} > > Maybe delete? Yes, meant to do that. > src/hotspot/share/prims/jvmtiTagMap.cpp line 131: > >> 129: // Operating on the hashmap must always be locked, since concurrent >> GC threads may >> 130: // notify while running through a safepoint. >> 131: assert(is_locked(), "checking"); > > Maybe move this to the top of the function to make it very clear. ok. > src/hotspot/share/prims/jvmtiTagMap.cpp line 133: > >> 131: assert(is_locked(), "checking"); >> 132: if (post_events && env()->is_enabled(JVMTI_EVENT_OBJECT_FREE)) { >> 133: log_info(jvmti, table)("TagMap table needs posting before heap >> walk"); > > Not sure about the "before heap walk" since this is also done from > GetObjectsWithTags, which does *not* do a heap walk but still requires > posting. I don't call check_hashmap for GetObjectsWithTags. > src/hotspot/share/prims/jvmtiTagMap.cpp line 140: > >> 138: hashmap()->rehash(); >> 139: _needs_rehashing = false; >> 140: } > > It's not clear to me that it's correct to rehash *after* posting. I think it > is, because unlink_and_post will use load barriers to fixup old pointers. I think it's better that the rehashing doesn't encounter null entries and the WeakHandle.peek() operation is used for both so I hope it would get the same answer. If not, which seems bad, the last answer should be what we hash on. > src/hotspot/share/prims/jvmtiTagMap.cpp line 144: > >> 142: >> 143: // This checks for posting and rehashing and is called from the heap >> walks. >> 144: // The ZDriver may be walking the hashmaps concurrently so all these >> locks are needed. > > Should this comment be moved down to the lock taking? ok, I also made it singular since Erik pointed out that we don't need the other lock. > src/hotspot/share/prims/jvmtiTagMap.cpp line 146: > >> 144: // The ZDriver may be walking the hashmaps concurrently so all these >> locks are needed. >> 145: void JvmtiTagMap::check_hashmaps_for_heapwalk() { >> 146: > > Extra white space. (Also double whitespace after this function) ? I removed the double whitespace after the function and put the whitespace here. It needs some whitespace. // This checks for posting and rehashing and is called from the heap walks. void JvmtiTagMap::check_hashmaps_for_heapwalk() { assert(SafepointSynchronize::is_at_safepoint(), "called from safepoints"); // Verify that the tag map tables are valid and unconditionally post events // that are expected to be posted before gc_notification. JvmtiEnvIterator it; > src/hotspot/share/prims/jvmtiTagMap.cpp line 377: > >> 375: MutexLocker ml(lock(), Mutex::_no_safepoint_check_flag); >> 376: >> 377: // Check if we have to processing for concurrent GCs. > > Sentence seems to be missing a few words. removed the sentence, because non concurrent GCs also defer rehashing to next use. > src/hotspot/share/prims/jvmtiTagMap.cpp line 954: > >> 952: o->klass()->external_name()); >> 953: return; >> 954: } > > Why is this done as a part of this RFE? Is this a bug fix that should be done > as a separate patch? Because it crashed with my changes and didn't without. I cannot recollect why. > src/hotspot/share/prims/jvmtiTagMap.cpp line 1152: > >> 1150: void JvmtiTagMap::unlink_and_post_locked() { >> 1151: MutexLocker ml(lock(), Mutex::_no_safepoint_check_flag); >> 1152: log_info(jvmti, table)("TagMap table needs posting before >> GetObjectTags"); > > There's no function called GetObjectTags. This log line needs to be adjusted. GetObjectsWithTags fixed. > src/hotspot/share/prims/jvmtiTagMap.cpp line 1162: > >> 1160: VMOp_Type type() const { return VMOp_Cleanup; } >> 1161: void doit() { >> 1162: _tag_map->unlink_and_post_locked(); > > Either inline unlink_and_post_locked() or updated gc_notification to use it? I thought of trying to share it but one logs and the other doesn't and it only saves 1 lines of code. > src/hotspot/share/prims/jvmtiTagMap.cpp line 1279: > >> 1277: // Can't post ObjectFree events here from a JavaThread, so this >> 1278: // will race with the gc_notification thread in the tiny >> 1279: // window where the oop is not marked but hasn't been notified that > > Please don't use "oop" when referring to "objects". fixed. > src/hotspot/share/prims/jvmtiTagMap.cpp line 2975: > >> 2973: } >> 2974: >> 2975: // Concurrent GC needs to call this in relocation pause, so after the >> oops are moved > > oops => objects fixed. > src/hotspot/share/prims/jvmtiTagMap.cpp line 2977: > >> 2975: // Concurrent GC needs to call this in relocation pause, so after the >> oops are moved >> 2976: // and have their new addresses, the table can be rehashed. >> 2977: void JvmtiTagMap::set_needs_processing() { > > Maybe rename to set_needs_rehashing? Since I went back and forth about what this function did (it posted events at one time), I thought the generic _processing name would be better. GC callers shouldn't really have to know what processing we're doing here. Hopefully it won't change from rehashing. That's why I like processing. > src/hotspot/share/prims/jvmtiTagMap.cpp line 2985: > >> 2983: >> 2984: JvmtiEnvIterator it; >> 2985: for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { > > The iterator seems fast enough, so it seems unnecessary to have the > environments_might_exist check. yes, it looks like it does the same thing. I'll remove it. > src/hotspot/share/prims/jvmtiTagMap.cpp line 2998: > >> 2996: // thread creation and before VMThread creation (1 thread); initial >> GC >> 2997: // verification can happen in that window which gets to here. >> 2998: if (!JvmtiEnv::environments_might_exist()) { return; } > > I don't know what this comment is saying, and why the code is needed. I've spent tons of time trying to understand this comment too. I think gc verification used to call oops do on the tagmap table. This comments obsolete now, and I'll remove it. > src/hotspot/share/prims/jvmtiTagMap.cpp line 3020: > >> 3018: JvmtiTagMap* tag_map = env->tag_map_acquire(); >> 3019: if (tag_map != NULL && !tag_map->is_empty()) { >> 3020: if (num_dead_entries > 0) { > > The other num_dead_entries check for != 0. Maybe use the same in the two > branches? ok. > src/hotspot/share/prims/jvmtiTagMap.cpp line 3023: > >> 3021: tag_map->hashmap()->unlink_and_post(tag_map->env()); >> 3022: } >> 3023: tag_map->_needs_rehashing = true; > > Maybe add a small comment why this is deferred. // Later GC code will relocate the oops, so defer rehashing until then. ? > src/hotspot/share/prims/jvmtiTagMap.hpp line 56: > >> 54: void entry_iterate(JvmtiTagMapEntryClosure* closure); >> 55: void post_dead_object_on_vm_thread(); >> 56: public: > > Looked nicer when there was a blank line before public. Now it looks like > public "relates" more to the code before than after. ok > src/hotspot/share/prims/jvmtiTagMap.hpp line 114: > >> 112: static void check_hashmaps_for_heapwalk(); >> 113: static void set_needs_processing() NOT_JVMTI_RETURN; >> 114: static void gc_notification(size_t num_dead_entries) NOT_JVMTI_RETURN; > > Have you verified that this builds without JVMTI? I will do (might have already done) that. Building non-oracle platforms builds minimal vm. > src/hotspot/share/prims/jvmtiTagMapTable.cpp line 50: > >> 48: // A subsequent oop_load without AS_NO_KEEPALIVE (the object() >> accessor) >> 49: // keeps the oop alive before doing so. >> 50: return literal().peek(); > > I'm not sure we should be talking about the low-level Access names. Maybe > reword in terms of WeakHandle operations? I'm going to say: // Just peek at the object without keeping it alive. > src/hotspot/share/prims/jvmtiTagMapTable.cpp line 81: > >> 79: void JvmtiTagMapTable::free_entry(JvmtiTagMapEntry* entry) { >> 80: unlink_entry(entry); >> 81: entry->literal().release(JvmtiExport::weak_tag_storage()); // release >> OopStorage > > release *to* OopStorage? fixed > src/hotspot/share/prims/jvmtiTagMapTable.cpp line 98: > >> 96: >> 97: // The obj is in the table as a target already >> 98: if (target != NULL && target == obj) { > > Wonder if we could assert that obj is not NULL at the entry of this function, > and then change this to simply target == obj? makes sense. > src/hotspot/share/prims/jvmtiTagMapTable.cpp line 122: > >> 120: int index = hash_to_index(hash); >> 121: // One was added while acquiring the lock >> 122: JvmtiTagMapEntry* entry = find(index, hash, obj); > > Should this be done inside ASSERT? yes. > test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/cm02t001.cpp > line 64: > >> 62: static jclass klass = NULL; >> 63: static jobject testedObject = NULL; >> 64: const jlong TESTED_TAG_VALUE = (5555555L); > > Remove parenthesis? I copied this from some other place that had parenthesis. ------------- PR: https://git.openjdk.java.net/jdk/pull/967