RFR: 8056240: Investigate increased GC remark time after class unloading changes in CRM Fuse
Stefan Karlsson
stefan.karlsson at oracle.com
Fri Oct 3 13:39:59 UTC 2014
On 2014-10-03 15:38, Stefan Karlsson wrote:
> Hi Betrand,
Sorry for misspelling your name, Bertrand.
StefanK
>
> Thanks for taking the time to review this.
>
> On 2014-10-03 14:42, Bertrand Delsart wrote:
>> Hi Stefan,
>>
>> I'll do a more in depth review next week but here are a few quick
>> comments to keep us busy in between :-)
>>
>> -1- The fact that set_on_stack() is now implemented by just a
>> atomic_try_set_bits seems dangerous.
>>
>> We could end up not setting the JVM_ACC_ON_STACK flag.
>>
>> In your case, you may be sure that the only concurrent operation that
>> might happen is a setting of the same flag but this looks error prone
>> with respect to future possible usages of that method (or new
>> concurrent operations).
>
> OK. I see your point about future-proofing this. Is this currently
> broken in that sense that two racing threads can both fail to set the
> on_stack bit?
>
>>
>> I would prefer to be sure the flag has really been set when exiting
>> from set_on_stack(), as was the case before.
>>
>> More generally, the semantic of the new atomic_try_set_bits is IMHO
>> dangerous. It returns false both if the bits were already set or if
>> another flag was changed concurrently (in which case the bits may not
>> be set). This should at least be clearly documented in the .hpp file.
>
>>
>>
>> An alternative would be for atomic_try_set_bits to return the current
>> value when the cas failed or was not performed. It would be up to the
>> caller to decide whether to loop or not (by checking whether the bits
>> are set in the returned value).
>
> I can change the code to use a slightly altered version of
> atomic_set_bit that returns 'true' if it was this thread that
> succeeded in setting the bit.
>
>>
>> -2- There is one part of the change that does not seem to be
>> described in the RFR or the CR. You added something to keep alive
>> some metadata in case of klass redefinition (the mark_on_stack stuff
>> in nmethod.cpp, which for instance now marks the content of the
>> CompiledICHolder).
>>
>> Did you just change when that marking was performed or is that a
>> marking that we were not doing previously ? (sorry, cannot review it
>> all currently and see whether that marking was indeed removed elsewhere)
>
> Before my change, this code was done by the call to:
> CodeCache::alive_nmethods_do(nmethod::mark_on_stack);
>
> if you look at:
> static void mark_on_stack(nmethod* nm) {
> nm->metadata_do(Metadata::mark_on_stack);
> }
>
> and then:
> // Iterate over metadata calling this function. Used by RedefineClasses
> void nmethod::metadata_do(void f(Metadata*)) {
> address low_boundary = verified_entry_point();
> if (is_not_entrant()) {
> low_boundary += NativeJump::instruction_size;
> // %%% Note: On SPARC we patch only a 4-byte trap, not a full
> NativeJump.
> // (See comment above.)
> }
> {
> // Visit all immediate references that are embedded in the
> instruction stream.
> RelocIterator iter(this, low_boundary);
> while (iter.next()) {
> if (iter.type() == relocInfo::metadata_type ) {
> metadata_Relocation* r = iter.metadata_reloc();
> // In this lmetadata, we must only follow those metadatas
> directly embedded in
> // the code. Other metadatas (oop_index>0) are seen as part of
> // the metadata section below.
> assert(1 == (r->metadata_is_immediate()) +
> (r->metadata_addr() >= metadata_begin() &&
> r->metadata_addr() < metadata_end()),
> "metadata must be found in exactly one place");
> if (r->metadata_is_immediate() && r->metadata_value() != NULL) {
> Metadata* md = r->metadata_value();
> if (md != _method) f(md);
> }
> } else if (iter.type() == relocInfo::virtual_call_type) {
> // Check compiledIC holders associated with this nmethod
> CompiledIC *ic = CompiledIC_at(&iter);
> if (ic->is_icholder_call()) {
> CompiledICHolder* cichk = ic->cached_icholder();
> f(cichk->holder_method());
> f(cichk->holder_klass());
> } else {
> Metadata* ic_oop = ic->cached_metadata();
> if (ic_oop != NULL) {
> f(ic_oop);
> }
> }
> }
> }
> }
>
> // Visit the metadata section
> for (Metadata** p = metadata_begin(); p < metadata_end(); p++) {
> if (*p == Universe::non_oop_word() || *p == NULL) continue; //
> skip non-oops
> Metadata* md = *p;
> f(md);
> }
>
> // Call function Method*, not embedded in these other places.
> if (_method != NULL) f(_method);
> }
>
> you see that the above calls to f(...) correspond to the added
> mark_on_stack sections in nmethod.cpp.
>
> So the code is now duplicated and added to places where they don't
> really belong, and that breaks some of the current abstraction. This
> is done just to be able to skip having to do an extra pass of the
> expensive "for all nmethods { for all relocInfos {} }" code. If we
> could get the RelocIterator code to become much cheaper, then we could
> remove all that added code and just use nmethod::metadata_do instead.
> That would also enable us to clean up some of the code complexity that
> has been added to the codecache/nmethod cleaning code.
>
> thanks,
> StefanK
>
>>
>> Regards,
>>
>> Bertrand
>>
>> On 02/10/14 12:53, Stefan Karlsson wrote:
>>> Hi all,
>>>
>>> (The following patch changes HotSpot code in areas concerning GC, RT,
>>> and Compiler. So, it would be good to get reviews from all three
>>> teams.)
>>>
>>> Please, review this patch to optimize and parallelize the CodeCache
>>> part
>>> of MetadaOnStackMark.
>>>
>>> G1 performance measurements showed longer than expected remark times on
>>> an application using a lot of nmethods and Metadata. The cause for this
>>> performance regression was the call to
>>> CodeCache::alive_nmethods_do(nmethod::mark_on_stack); in
>>> MetadataOnStackMark. This code path is only taken when class
>>> redefinition is used. Class redefinition is typically used in
>>> monitoring
>>> and diagnostic frameworks.
>>>
>>> With this patch we now:
>>> 1) Filter out duplicate Metadata* entries instead of storing a
>>> Metadata*
>>> per visited metadata reference.
>>> 2) Collect the set of Metadata* entries in parallel. The code
>>> piggy-backs on the parallel CodeCache cleaning task.
>>>
>>> http://cr.openjdk.java.net/~stefank/8056240/webrev.00/
>>> https://bugs.openjdk.java.net/browse/JDK-8056240
>>>
>>> Functional testing:
>>> JPRT, Kitchensink, parallel_class_unloading, unit tests
>>>
>>> Performance testing:
>>> CRM Fuse - where the regression was found
>>>
>>> The patch changes HotSpot code in areas concerning GC, RT, Compiler,
>>> and
>>> Serviceability. It would be good to get some reviews from the other
>>> teams, and not only from the GC team.
>>>
>>> thanks,
>>> StefanK
>>
>>
>
More information about the hotspot-dev
mailing list