RFR 8165246: [REDO] InstanceKlass::_previous_version_count goes negative

Daniel D. Daugherty daniel.daugherty at oracle.com
Wed Sep 7 16:46:52 UTC 2016


I'm good with all of your proposed changes.

Dan


On 9/7/16 10:42 AM, Coleen Phillimore wrote:
>
>
> On 9/7/16 11:36 AM, Daniel D. Daugherty wrote:
>> On 9/6/16 10:24 AM, Coleen Phillimore wrote:
>>> Summary: make _has_previous_version a boolean that is set to true 
>>> when previous version of a class is added or during class unloading 
>>> call to purge_previous_versions
>>>
>>> The failed fix was because InstanceKlass's previous_versions klasses 
>>> were walked more than once during class unloading, because the 
>>> scratch classes are also on the CLD::_klasses list. This change does 
>>> three things.  1. Only walk the class if has_been_redefined (which 
>>> is not set for the scratch class), 2. set next link to NULL for the 
>>> scratch class when removed from the previous version list, and 3. 
>>> change the counter to a flag because the counter may never go to 
>>> zero if a CLD has redefined classes with previous versions and is 
>>> unloaded.  For this latter case we don't need to walk 
>>> previous_versions.
>>>
>>> I tested this with changes 1 and 2 in Kitchensink, to verify the 
>>> diagnosis, and then added 3 and retested.
>>> Also tested with the fix for 
>>> https://bugs.openjdk.java.net/browse/JDK-8156137 which was checked 
>>> into hs_-omp repository.
>>>
>>> Also ran through RBT 4 tiers nightly tests.
>>>
>>> open webrev at http://cr.openjdk.java.net/~coleenp/8165246.01/webrev
>>
>> src/share/vm/oops/instanceKlass.hpp
>>     No comments.
>>
>> src/share/vm/oops/instanceKlass.cpp
>>     L3370: // Globally, there is at least one previous version of a 
>> class to walk
>>     L3371: // during class unloading, which is saved because there 
>> are old methods in the class
>>     L3372: // are still running.   Otherwise the previous version 
>> list is cleaned up.
>>         Perhaps a little tweaking of the comment:
>>
>>            // Globally, there is at least one previous version of the 
>> class to walk
>>            // during class unloading, which was saved because there 
>> were old methods
>>            // in the class that are still running. Otherwise the 
>> previous version
>>            // list is cleaned up.
>
> George had the same comment, so I fixed the grammar there.
>>
>>     L3375: // Returns whether there are previous versions of a class 
>> for class
>>     L3376: // unloading only.  This resets the value to false. 
>> purge_previous_versions
>>     L3377: // will set it to true if there are any left, i.e. if 
>> there's any work to do for
>>     L3378: // next time.  This is to avoid the expensive code cache 
>> walk in CLDG::do_unloading.
>>         Perhaps a little tweaking of the comment:
>>
>>            // Returns true if there are previous versions of a class 
>> for class
>>            // unloading only. Also resets the flag to false. 
>> purge_previous_versions
>>            // will set the flag to true if there are any left, i.e., 
>> if there's any
>>            // work to do for next time. This is to avoid the 
>> expensive code cache
>>            // walk in CLDG::do_unloading().
>>
> Duly tweaked.  I also renamed the function 
> has_previous_versions_and_reset().
>
>>     L3379: bool InstanceKlass::has_previous_versions() {
>>     L3383:   _has_previous_versions = false;
>>         Named like a query function, but it has a side effect.
>>         Perhaps: has_previous_versions_and_reset()?
>>
>>     L3391:   assert (has_been_redefined(), "Should only be called for 
>> main class");
>>         Nit: please delete the space before '('.
>>
>
> Fixed.
>
>>     L3427: loader_data->add_to_deallocate_list(pv_node);
>>     L3428:         InstanceKlass* next = pv_node->previous_versions();
>>     L3429:         pv_node->link_previous_versions(NULL); // point 
>> next to NULL
>>     L3430:         last->link_previous_versions(next);
>>         The pv_node to be deleted is added to the deallocate list
>>         before it is decoupled from chain. Perhaps it should be
>>         added after it is decoupled to permit parallel processing
>>         of the deallocate list, i.e., move L3427 after L3430. I
>>         don't think it is processed in parallel now, but down the
>>         road...
>
> Okay, this seems like a good suggestion.  It's not processed in 
> parallel now but yeah, you never know.
>
> I did this carefully and added a comment.
>
>         log_trace(redefine, class, iklass, purge)
>           ("previous version " INTPTR_FORMAT " is dead.", p2i(pv_node));
>         // For debugging purposes.
>         pv_node->set_is_scratch_class();
>         // Unlink from previous version list.
>         assert(pv_node->class_loader_data() == loader_data, "wrong 
> loader_data");
>         InstanceKlass* next = pv_node->previous_versions();
>         pv_node->link_previous_versions(NULL);   // point next to NULL
>         last->link_previous_versions(next);
>         // Add to the deallocate list after unlinking.
>         loader_data->add_to_deallocate_list(pv_node);
>         pv_node = next;
>         deleted_count++;
>         version++;
>
>>
>>     L3592:   // Add previous version if any methods are still running.
>>     L3593:   _has_previous_versions = true;
>>         In the other place you set the flag to true, you mention
>>         that this is for class unloading. Do you want to say that
>>         here also?
>
> ok, added:
>
>   // Set has_previous_version flag for processing during class unloading.
>
>>
>>
>> test/runtime/RedefineTests/RedefinePreviousVersions.java
>>     L26:  * @bug 8164692
>>         Please change the bug ID to 8165246.
>
> thanks, missed that.
>>
>>     L27:  * @summary Redefine previous_versions count goes negative
>>         Need to update the test summary.
>
>  * @summary Test has_previous_versions flag and processing during 
> class unloading.
>
>>
>>     L88:         // previous_version_list and the count should stay 
>> zero and not go negative
>>         Need to update the comment since there is no longer
>>         a counter.
>
>         // previous_version_list and the flag _has_previous_versions 
> should stay false
>
> I reused the test so missed the old comments.  Thanks for pointing 
> them out.
>>
>> OK so the whole reason for InstanceKlass::has_previous_versions() is
>> an optimization so that we don't have to do an expensive code cache
>> walk for previous versions during class unloading. The old code
>> implemented the "state of having a previous version" as a counter.
>> The new code implements the "state of having a previous version"
>> as a flag that is only set when we add a previous version (yea!)
>> and cleared when it is queried. Since there is no more counter,
>> we won't run into the bug we're trying to fix.
>>
>> The only thing that bugs me is this:
>>
>>   bool v1 = has_previous_versions();
>>   bool v2 = has_previous_versions();
>>
>> I would expect (v1 == v2) which isn't what you get.
>> Perhaps renaming from has_previous_versions() to
>> has_previous_versions_and_reset() will make it clear
>> that there is a side effect...
>>
>
> Yes, this is a bit dangerous to assume that two calls to 
> has_previuos_verisons() would have the same result.  I renamed the 
> function as suggested.
>
> Thanks for the thorough review!
>
> Coleen
>
>> Dan
>>
>>
>>> bug link https://bugs.openjdk.java.net/browse/JDK-8165246
>>>
>>> Thanks,
>>> Coleen
>>>
>>>
>>
>



More information about the hotspot-runtime-dev mailing list