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:38:18 UTC 2014


Hi Betrand,

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