RFR 8055008: Clean up code that saves the previous versions of redefined classes

Daniel D. Daugherty daniel.daugherty at oracle.com
Tue Sep 2 17:42:50 UTC 2014


On 8/28/14 2:39 PM, Coleen Phillimore wrote:
>
> Serguei,   Thank you for the code review and discussions!
>
> This led me to find a bug and here is the new webrev, but there are 
> comments below to explain:
>
> open webrev at http://cr.openjdk.java.net/~coleenp/8055008_3/

src/share/vm/oops/instanceKlass.hpp
     No comments.

src/share/vm/oops/instanceKlass.cpp
     No comments.

src/share/vm/oops/method.hpp
     No comments.

src/share/vm/classfile/metadataOnStackMark.hpp
     No comments.

src/share/vm/classfile/metadataOnStackMark.cpp
     No comments.

src/share/vm/classfile/classLoaderData.cpp
     No comments.

src/share/vm/prims/jvmtiRedefineClasses.hpp
     No comments.

src/share/vm/prims/jvmtiRedefineClasses.cpp
     line 138:   MetadataOnStackMark md_on_stack(true);
         So this "new has_redefined_a_class" parameter that we're
         passing "true" here allows us to call CodeCache::
         alive_nmethods_do() earlier. That call was previously keyed
         off a call to JvmtiExport::has_redefined_a_class() which
         wouldn't be true until after the first round of
         RedefineClasses() was pretty much done.

         So I assume we were not updating some things in the
         CodeCache during our first RedefineClasses() call so we
         had some incorrect (probably obsolete) methods being called?

         Update: Now that I've read the replies below the webrev
         link I see that my questions are already answered.

src/share/vm/prims/jvmtiImpl.cpp
     No comments.

src/share/vm/code/nmethod.cpp
     No comments.

src/share/vm/memory/universe.cpp
     No comments.

test/runtime/RedefineTests/RedefineFinalizer.java
     No comments.

test/runtime/RedefineTests/RedefineRunningMethods.java
     No comments.

Thumbs up!

Dan


>
>
> On 8/27/14, 8:20 AM, serguei.spitsyn at oracle.com wrote:
>> Hi Coleen,
>>
>>
>> src/share/vm/code/nmethod.cpp
>>
>>   Nice simplification.
>>
>>
>> src/share/vm/memory/universe.cpp
>>
>>   No comments
>>
>>
>> src/share/vm/oops/instanceKlass.cpp
>>
>>   A minor question about two related fragments:
>>
>> 3505       // next previous version
>> 3506       last = pv_node;
>> 3507       pv_node = pv_node->previous_versions();
>> 3508       version++;
>>
>>   Should the version be incremented to the case 3462-3469 as at the 
>> line 3508?
>>   It is not a big issue as the version number is used at the RC_TRACE 
>> line only:
>>      3496 method->signature()->as_C_string(), j, version));
>>
>>
>
> Yes, I fixed that.
>
>>   We still have no consensus on the following question:
>>   Can a non-running EMCP method become running again after the flag 
>> was cleared?
>>
>> 3487           if (!method->on_stack()) {
>> 3488             if (method->is_running_emcp()) {
>> 3489               method->set_running_emcp(false);  // no 
>> breakpoints for non-running methods
>> 3490             }
>>
>>   Just wanted to be sure what is the current view on this. :)
>>
>
> Not unless there is a bug, such that we hit a safepoint with the EMCP 
> method not in any place that MetadataOnStackMark can get to it but we 
> have a Method* pointer to it.  This situation could result in the EMCP 
> Method* getting deallocated as well.
>
> Stefan filed a bug a while ago that Method* pointers are not safe 
> without a methodHandle but we currently don't have any verification 
> that they are properly handled, such as CheckUnhandledOops.
>
>>
>> src/share/vm/oops/instanceKlass.hpp
>>
>>   No comments
>>
>>
>> src/share/vm/oops/method.hpp
>>
>>   Just some questions.
>>   Usefulness of this new function depends on basic ability of a 
>> non-running method to become running again:
>>        is_running_emcp()
>>
>>   The questions are:
>>    - How precise is the control of this bit?
>
> This bit is set during purging previous versions when all methods have 
> been marked on_stack() if found in various places.  The bit is only 
> used for setting breakpoints.
>
>>    - Should we clear this bit after all method invocations have been 
>> finished?
>>    - Can a EMCP method become running again after the bit was cleared 
>> or not set?
>>
>>
>> src/share/vm/prims/jvmtiImpl.cpp
>>
>>   300       if (method->is_running_emcp() &&
>>
>>    Is it possible that an EMCP method becomes running after the bit 
>> is_running_emcp() is set?
>>     Do we miss breakpoints in such a case?
>>
>
> I do think this is only possible if there is a bug and the Method* 
> would probably be deallocated first and crash.  Since this code is 
> called during class unloading, a crash is something I'd expect to see.
>
> So I added an additional check to find resurrected emcp methods:
>
>           if (!method->on_stack()) {
>             if (method->is_running_emcp()) {
>               method->set_running_emcp(false); // no breakpoints for 
> non-running methods
>             }
>           } else {
>             assert (method->is_obsolete() || method->is_running_emcp(),
>                     "emcp method cannot run after emcp bit is cleared");
>             // RC_TRACE macro has an embedded ResourceMark
>             RC_TRACE(0x00000200,
>               ("purge: %s(%s): prev method @%d in version @%d is alive",
>
>
> Unfortunately, this assert did fire in NSK testing.  And I just now 
> found the bug and fixed it (see above webrev).  We weren't walking the 
> code cache for the first redefinition, and an emcp method was in the 
> code cache.  The methods in the code cache are not exactly running but 
> we don't know if they could be called directly by running compiled code.
>
> This fix is to add a boolean flag to MetadataOnStackMark.  I reran the 
> consistently failing tests, nsk.quick tests, and the jtreg tests in 
> java/lang/instrument on the fix.
>>
>>  303         RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)",
>>  304           meth_act == &Method::set_breakpoint ? "set" : "clear",
>>
>>    The change from  "sett" to "set" seems to be wrong (see the line 
>> 303):
>>
>
> Yes, I didn't realize that.  Dan pointed this out to me also.
>>
>> src/share/vm/prims/jvmtiRedefineClasses.cpp
>> src/share/vm/prims/jvmtiRedefineClasses.hpp
>>
>>   No comments
>>
>
> Thank you Serguei for the in-depth comments and pushing on these 
> issues.  I think this has improved the code tremendously.  I really 
> appreciate it!
>
> Coleen
>>
>> Thanks,
>> Serguei
>>
>>
>> On 8/22/14 7:26 AM, Coleen Phillimore wrote:
>>>
>>> Thanks Dan, Serguei and Roland for discussion on this change. The 
>>> latest version is here:
>>>
>>> open webrev at http://cr.openjdk.java.net/~coleenp/8055008_2/
>>>
>>> Changes from the last version (don't have the setup to do a diff 
>>> webrev, sorry) are that I have a new flag to mark running emcp 
>>> methods so we can set breakpoints in only those.  Also, confirmed 
>>> that we need to clean_weak_method_links in obsolete methods too. 
>>> Made changes per review comments.  Also, added more to the test so 
>>> that all tracing in InstanceKlass comes out. Reran all tests (nsk, 
>>> jck, jtreg, java/lang/instrument).
>>>
>>> Thanks to whoever made command line processing handle hex numbers!
>>>
>>> Thanks,
>>> Coleen
>>>
>>> On 8/20/14, 9:26 PM, Coleen Phillimore wrote:
>>>>
>>>> On 8/20/14, 6:45 PM, Daniel D. Daugherty wrote:
>>>>> On 8/20/14 2:01 PM, Coleen Phillimore wrote:
>>>>>> On 8/20/14, 3:49 PM, serguei.spitsyn at oracle.com wrote:
>>>>>>>>
>>>>>>>> If an EMCP method is not running, should we save it on a 
>>>>>>>> previous version list anyway so that we can make it obsolete if 
>>>>>>>> it's redefined and made obsolete?
>>>>>>>
>>>>>>> I hope, Dan will catch me if I'm wrong...
>>>>>>>
>>>>>>> I think, we should not.
>>>>>>> An EMCP method can not be made obsolete if it is not running.
>>>>>>>
>>>>>>
>>>>>>
>>>>>> It should be this way otherwise we'd have to hang onto things 
>>>>>> forever.
>>>>>
>>>>> An EMCP method should only be made obsolete if a RedefineClasses() or
>>>>> RetransformClasses() operation made it so. We should not be 
>>>>> leveraging
>>>>> off the obsolete-ness attribute to solve a life-cycle problem.
>>>>
>>>> Yes, this was my error in the change.  This is why I made things 
>>>> obsolete if they were not running.  I think I can't reuse this 
>>>> flag.  My latest changes add a new explicit flag (which we have 
>>>> space for in Method*).
>>>>>
>>>>> In the pre-PGR world, we could trust GC to make a completely unused
>>>>> EMCP method collectible and eventually our weak reference would go
>>>>> away. Just because an EMCP method is not on a stack does not mean
>>>>> that it is not used so we need a different way to determine whether
>>>>> it is OK to no longer track an EMCP method.
>>>>
>>>> Our on_stack marking is supposed to look at all the places where GC 
>>>> used to look so I think we can use on_stack to track the lifecycle 
>>>> of EMCP methods.  If the EMCP method is somewhere, we will find it!
>>>>
>>>> I'm running tests on the latest change, but am also waiting for 
>>>> confirmation from Roland because we were only cleaning out 
>>>> MethodData for EMCP methods and not for running obsolete methods 
>>>> and I think we need to do that for obsolete methods also, which my 
>>>> change does now.  I think it was a bug.
>>>>
>>>> Thanks Dan for remembering all of this for me!
>>>>
>>>> Coleen
>>>>>
>>>>>
>>>>>>
>>>>>>> BTW, I'm reviewing the webrev too, but probably it'd be better 
>>>>>>> to switch to updated webrev after it is ready.
>>>>>>
>>>>>> Yes, this is a good idea.  I figured out why I made emcp methods 
>>>>>> obsolete, and I'm fixing that as well as Dan's comments. Thanks!
>>>>>
>>>>> Cool! I'm looking forward to the next review.
>>>>>
>>>>> Dan
>>>>>
>>>>>
>>>>>>
>>>>>> Coleen
>>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Serguei
>>>>>>
>>>>>
>>>>
>>>
>>
>



More information about the hotspot-dev mailing list