RFR: 8056240: Investigate increased GC remark time after class unloading changes in CRM Fuse

Stefan Karlsson stefan.karlsson at oracle.com
Mon Oct 13 09:28:43 UTC 2014


On 2014-10-11 00:09, Coleen Phillimore wrote:
>
> In nmethod.cpp how is this change related?  ie. the added function and 
> call.  Was it a cleanup?
>
> nmethod::unload_if_dead_at()

I had to introduce the mark on stack code in the 
relocInfo::metadata_type. And instead of writing it inline in the 
RelocIterator loop, I opted for putting it in a function. To make the 
code clearer I also moved the code in relocInfo::oop_type to a new function.

The code now looks like:

1951     case relocInfo::oop_type:
1952       if (!is_unloaded) {
1953         is_unloaded = unload_if_dead_at(&iter, is_alive, unloading_occurred);
1954       }
1955       break;
1956
1957     case relocInfo::metadata_type:
1958       if (mark_metadata_on_stack) {
1959         mark_metadata_on_stack_at(&iter);
1960       }

instead of having nitty-gritty relocation code in the loop.

thanks,
StefanK

>
> thanks,
> Coleen
>
> On 10/10/14, 4:54 PM, Coleen Phillimore wrote:
>>
>> Hi Stefan,
>>
>> Thank you for your patience.  Some review comments:
>>
>> http://cr.openjdk.java.net/~stefank/8056240/webrev.00/src/share/vm/classfile/metadataOnStackMark.hpp.udiff.html 
>>
>>
>> Why is there all this code here?  In order to make this parallel, can 
>> you just not have simply a GrowableArray per thread?  I don't see the 
>> purpose of all this complexity in this place.  Is it trying to add as 
>> chunked list?   Can we add a utility for this instead of tying it to 
>> the MetadataOnStackMark functionality?
>>
>> In this file, what is _doit and why?   This looks really strange. Can 
>> you add a comment why sometimes doit is false and sometimes doit is 
>> true?  Is there a way to encapsulate this in a function as it's 
>> supposed to be a scoped object.
>>
>> The MetadataOnStackMark object needs to go around the calls to 
>> purge_previous_versions and free_deallocate_list.  It doesn't have to 
>> be around the whole walk of the CLDG, it just started out that way 
>> and has gotten more complicated.  If you add it to the end like:
>>
>> void ClassLoaderDataGraph::clean_metaspaces() {
>>   // mark metadata seen on the stack and code cache so we can delete 
>> unneeded entries.
>>   bool has_redefined_a_class = JvmtiExport::has_redefined_a_class();
>>   MetadataOnStackMark md_on_stack(has_redefined_a_class);
>>   if (has_redefined_a_class) {
>>     // purge_previous_versions also cleans weak method links. Because
>>     // one method's MDO can reference another method from another
>>     // class loader, we need to first clean weak method links for all
>>     // class loaders here. Below, we can then free redefined methods
>>     // for all class loaders.
>>     for (ClassLoaderData* data = _head; data != NULL; data = 
>> data->next()) {
>> data->classes_do(InstanceKlass::purge_previous_versions);
>>     }
>>   }
>>   // Need two loops.
>>   for (ClassLoaderData* data = _head; data != NULL; data = 
>> data->next()) {
>>     data->free_deallocate_list();
>>   }
>> }
>>
>> And only call it at the end of do_unloading this would work (I've 
>> tested this)
>>
>>   if (has_metadata_to_deallocate) {
>>     clean_metaspaces();
>>   }
>>
>> has_metadata_to_deallocate is generally true because of lambda 
>> constant pools.
>>
>> http://cr.openjdk.java.net/~stefank/8056240/webrev.00/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp.udiff.html 
>>
>>
>> +    if (JvmtiExport::has_redefined_a_class()) {
>> +      InstanceKlass::purge_previous_versions(ik);
>> +    }
>>
>>
>> purge previous versions requires the on_stack bits to be set. Is this 
>> true here?  It seems very far away from the other code to me to be 
>> able to tell.   Why can't this go in the same place as the 
>> free_deallocate_lists?
>>
>> http://cr.openjdk.java.net/~stefank/8056240/webrev.00/src/share/vm/code/nmethod.cpp.udiff.html 
>>
>>
>> I really don't like that the on-stack bit marks are done at the same 
>> time as clean_ic_if_metadata_is_dead.   They are for different 
>> purposes.  Also, this seems to mark metadata for methods that are 
>> unloaded as well as ones that survive unloading.  I guess this 
>> wouldn't prevent methods from being unloaded but seems unnecessary 
>> callbacks to mark_on_stack. Maybe I'm not reading this right though.  
>> I thought you couldn't do the unloading walk with the mark-on-stack 
>> walk?   Also, we actually only have to mark OLD methods in the code 
>> cache which would greatly reduce the callbacks. I could make this in 
>> a separate change as I have this change in a private repository.
>>
>> I think you should have a compiler reviewer comment on the nmethod 
>> code though.
>>
>> http://cr.openjdk.java.net/~stefank/8056240/webrev.00/src/share/vm/utilities/accessFlags.cpp.udiff.html 
>>
>>
>> I don't know why this needs to be different than the atomic_set_bits 
>> function.  I should have made on_stack be a flag in Method* rather 
>> than in access flags.
>>
>> There's a lot here.  I'm glad it helps performance and making the 
>> code cache walk parallel is a really good change.
>>
>> Thanks,
>> Coleen
>>
>>
>> On 10/2/14, 6:53 AM, 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