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

Coleen Phillimore coleen.phillimore at oracle.com
Wed Sep 7 18:16:50 UTC 2016



On 9/7/16 12:46 PM, Daniel D. Daugherty wrote:
> I'm good with all of your proposed changes.

Thank you Dan for the detailed review.  I'll retest the change with some 
redefine tests, just to make sure I didn't break anything.
Coleen

>
> 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