Re: 答复: 答复: HotSpot and IBM's J9 behave quite differently when processing monitorenters and monitorexits

Mikael Gerdin mikael.gerdin at oracle.com
Thu May 18 14:25:45 UTC 2017


Hi,

On 2017-05-18 16:22, 陈雨亭 wrote:
> HotSpot throws a NullPointerException correctly when an initialized object as set as null, as follows.
> 
> public void main(java.lang.String[]) throws java.lang.Exception;
>      descriptor: ([Ljava/lang/String;)V
>      flags: ACC_PUBLIC
>      Code:
>        stack=1, locals=2, args_size=2
>           0: new           #2                  // class Search
>           3: invokespecial #14                 // Method "<init>":()V
>           6: aconst_null
>           7: monitorenter
>           8: aconst_null
>           9: monitorexit
>          10: return
>      Exceptions:
>        throws java.lang.Exception
> 

Right. I just realized I missed the fact that the code did end up 
dereferencing the object but the code which actually makes that happen 
is not particularly easy to follow.
Sorry for the noise.
/Mikael

> -----邮件原件-----
> 发件人: Mikael Gerdin [mailto:mikael.gerdin at oracle.com]
> 发送时间: 2017年5月18日 4:55
> 收件人: David Holmes <david.holmes at oracle.com>; chenyt <chenyt at cs.sjtu.edu.cn>
> 抄送: hotspot-runtime-dev at openjdk.java.net
> 主题: Re: 答复: HotSpot and IBM's J9 behave quite differently when processing monitorenters and monitorexits
> 
> 
> 
> On 2017-05-18 07:25, David Holmes wrote:
>> Hi Yuting,
>>
>> On 18/05/2017 2:49 PM, chenyt wrote:
>>> Hi, David,
>>>
>>> I tested the next program (let one object to be monitored be null)
>>> using
>>> J9 and HotSpot. HotSpot does not throw a NullPointerException, as the
>>> specification says. J9 looks fine (throw a NullPointerException). Am
>>> I wrong here?
>>
>> I can't readily test this as I don't have Jimple nor quick and easy
>> access to bytecode assemblers. It is strange though as the interpreter
>> code contains this:
>>
>>        CASE(_monitorenter): {
>>          oop lockee = STACK_OBJECT(-1);
>>          // derefing's lockee ought to provoke implicit null check
>>          CHECK_NULL(lockee);
> 
> In the x86 templateTable we do
> 
> void TemplateTable::monitorenter() {
>     transition(atos, vtos);
> 
>     // check for NULL object
>     __ null_check(rax);
> 
> 
> but looking in macroAssembler_x86.cpp null_check only does a cmp (setting the eflags) but there's no condtional branch on the Z flag?
> 
> It feels like I'm crazy but several of these __ null_check() calls seem broken to me.
> 
> /Mikael
> 
>>
>> If I can find some time I will try to test this myself.
>>
>>> It is very interesting that I have found so many unexpected behaviors
>>> here.
>>
>> Well you only found two and they are related. :) Manually assembled
>> monitor code is not something very many (any?) people care about or do
>> - other than emulating correct language usage.
>>
>> Cheers,
>> David
>>
>>>    public void main(java.lang.String[]) throws java.lang.Exception;
>>>      descriptor: ([Ljava/lang/String;)V
>>>      flags: ACC_PUBLIC
>>>      Code:
>>>        stack=1, locals=2, args_size=2
>>>           0: aload_0
>>>           1: monitorenter
>>>           2: aconst_null
>>>           3: monitorenter
>>>           4: aconst_null
>>>           5: monitorexit
>>>           6: aload_0
>>>           7: monitorexit
>>>           8: return
>>>      Exceptions:
>>>        throws java.lang.Exception
>>>
>>> Jimple code is given as follows:
>>> public void main(java.lang.String[]) throws java.lang.Exception
>>>      {
>>>          Search r0;
>>>          Search r2;
>>>          java.lang.String[] r1;
>>>
>>>          r0 := @this: Search;
>>>          r1 := @parameter0: java.lang.String[];
>>>          r2 = null;
>>>          entermonitor r0;
>>>          entermonitor r2;
>>>          exitmonitor r2;
>>>          exitmonitor r0;
>>>
>>>          return;
>>>      }
>>>
>>> Wishes,
>>> Yuting
>>>
>>>
>>> On Thu, 18 May 2017 13:23:09 +1000, David Holmes wrote:
>>>> One correction ...
>>>>
>>>> On 18/05/2017 10:29 AM, David Holmes wrote:
>>>>> On 18/05/2017 8:12 AM, 陈雨亭 wrote:
>>>>>> Thank you, David. I have seen from the specification that
>>>>>> structured locking enforcement is optional. The second and the
>>>>>> third ones are cases of structured/nested lockings. Will
>>>>>> non-nested locking sequences raise deadlocks? Of course it is a
>>>>>> different topic, while it might be better if it can be kicked out
>>>>>> earlier from the specification/JVM.
>>>>>
>>>>> Non-nested locking doesn't necessarily lead to deadlocks - you just
>>>>> need a different locking order for that (even if properly nested).
>>>>> There are locking patterns that rely on the ability to lock and
>>>>> unlock in different order ie chained-locking for walking
>>>>> linked-lists A->B->C->D:
>>>>> - lock A, lock B, unlock A, lock C, unlock B, lock D, unlock C ...
>>>>>
>>>>> The VM spec allows a little flexibility in how the monitor
>>>>> bytecodes can be used compared to the Java programming language.
>>>>> That's not something that will change.
>>>>>
>>>>>> The first example is still a problem. It seems that HotSpot allows
>>>>>> to monitor a pure object reference without initialized (Is it
>>>>>> true? How can this checking be omitted?). J9 reports a verifyerror
>>>>>> as follows.
>>>>>>
>>>>>> Exception in thread "main" java.lang.VerifyError: JVMVRFY012 stack
>>>>>> shape inconsistent; class=Search,
>>>>>> method=main([Ljava/lang/String;)V, pc=6 Exception Details:
>>>>>>    Location:
>>>>>>      Search.main([Ljava/lang/String;)V @6: JBmonitorenter
>>>>>>    Reason:
>>>>>>      Type 'uninitialized' (current frame, stack[1]) is not
>>>>>> assignable to 'java/lang/Object'
>>>>>>    Current Frame:
>>>>>>      bci: @6
>>>>>>      flags: { }
>>>>>>      locals: { 'Search', '[Ljava/lang/String;' }
>>>>>>      stack: { 'uninitialized', 'uninitialized' }
>>>>>>      at T.main(T.java:4)
>>>>>
>>>>> Yes I think this may be a bug in hotspot. The type-checking for the
>>>>> monitor bytecodes requires a matching type of reference on the
>>>>> operand stack - but "uninitialized" does not match Object, as J9
>>>>> reports. But I'm not an expert on this aspect of verification so I
>>>>> may not be interpreting it correctly.
>>>>>
>>>>> More below ...
>>>>>
>>>>>> -----邮件原件-----
>>>>>> 发件人: David Holmes [mailto:david.holmes at oracle.com]
>>>>>> 发送时间: 2017年5月17日 14:41
>>>>>> 收件人: 陈雨亭 <chenyt at cs.sjtu.edu.cn>;
>>>>>> hotspot-runtime-dev at openjdk.java.net
>>>>>> 主题: Re: HotSpot and IBM's J9 behave quite differently when
>>>>>> processing monitorenters and monitorexits
>>>>>>
>>>>>> Hi,
>>>>>> On 18/05/2017 7:23 AM, 陈雨亭 wrote:
>>>>>>> Am I wrong?
>>>>>>
>>>>>> I will look at each situation in detail when I get a chance but
>>>>>> structured locking enforcement is optional. Also balancing the
>>>>>> number of locks and unlocks in a frame does not mean they can't be
>>>>>> locked and unlocked in a non-nested fashion - just that by the end
>>>>>> the number of unlocks matches the number of locks.
>>>>>>
>>>>>> BTW the way you respond to these emails, as if having a
>>>>>> conversation with yourself, makes it difficult to respond as we
>>>>>> can't readily see what is the new email and what is the original.
>>>>>>
>>>>>> Cheers,
>>>>>> David
>>>>>>
>>>>>>> The byte code for main() in case 1 is as follows. The strange
>>>>>>> thing is that NullPointerException is also not thrown at runtime.
>>>>>
>>>>> That is strange as it does for the normal obvious case of using
>>>>> synchronized(o) when o is null.
>>>>
>>>> Ah - it isn't null it just an object for which the constructor has
>>>> not been run. The runtime can't tell the difference between a
>>>> pointer to a valid initialized object, and a pointer to an
>>>> uninitialized chunk of memory.
>>>>
>>>>>>>
>>>>>>> public void main(java.lang.String[]) throws java.lang.Exception;
>>>>>>>      descriptor: ([Ljava/lang/String;)V
>>>>>>>      flags: ACC_PUBLIC
>>>>>>>      Code:
>>>>>>>        stack=3, locals=2, args_size=2
>>>>>>>           0: new           #2                  // class Search
>>>>
>>>> This allocated an object - hence no null reference. But this is what
>>>> verification should have complained about.
>>>>
>>>> David
>>>> -----
>>>>
>>>>>>>           3: dup
>>>>>>>           4: aload_0
>>>>>>>           5: monitorenter
>>>>>>>           6: monitorenter
>>>>>>>           7: monitorexit
>>>>>>>           8: aload_0
>>>>>>>           9: monitorexit
>>>>>>>          10: return
>>>>>>>      Exceptions:
>>>>>>>        throws java.lang.Exception
>>>>>>>
>>>>>>> 主题: HotSpot and IBM's J9 behave quite differently when processing
>>>>>>> monitorenters and monitorexits
>>>>>>>
>>>>>>> I have tested several programs (in Jimple) and found that HotSpot
>>>>>>> and
>>>>>>> J9 match monitorenters and monitorexits quite differently.
>>>>>>> Verifiers should play more important roles here.
>>>>>
>>>>> The job of the verifier is to establish some basic guarantees for
>>>>> the JVM to then operate under. The verifier plays no role in
>>>>> checking how monitorenter/exit are used in combination, only that
>>>>> each individual bytecode meets some basic type constraints.
>>>>>
>>>>>>>
>>>>>>> (1) Test the next program (r2 is not initizlied) on HotSpot and J9.
>>>>>>> J9 throw out a verifier error, while HotSpot does not. It seems
>>>>>>> that HotSpot's verifier forgets to check whether a monitored
>>>>>>> object is initialized.
>>>>>>>
>>>>>>> public class Search extends java.lang.Object { public void <init>()
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          r0 := @this: Search;
>>>>>>>          specialinvoke r0.<java.lang.Object: void <init>()>();
>>>>>>>          return;
>>>>>>>      }
>>>>>>> public void main(java.lang.String[]) throws java.lang.Exception
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          Search r2;
>>>>>>>          java.lang.String[] r1;
>>>>>>>          r0 := @this: Search;
>>>>>>>          r1 := @parameter0: java.lang.String[];
>>>>>>>          r2 = new Search;
>>>>>>>
>>>>>>>          entermonitor r2;
>>>>>>>          entermonitor r0;
>>>>>>>          exitmonitor r2;
>>>>>>>          exitmonitor r0;
>>>>>>>          return;
>>>>>>>      }
>>>>>>> }
>>>>>
>>>>> Verification was covered above.
>>>>>
>>>>>>>
>>>>>>> (2) Test the next program on HotSpot and J9, and both do not
>>>>>>> report any errors. However, I guess the order in the program
>>>>>>> (entermonitor r2; => entermonitor r0; =>  exitmonitor r2; =>
>>>>>>> exitmonitor r0;) violates the situation of "structured locking"
>>>>>>> (Structured locking is the situation when, during a method
>>>>>>> invocation, every exit on a given monitor matches a preceding
>>>>>>> entry on that monitor, see the specification
>>>>>>>
>>>>>>> https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.
>>>>>>>
>>>>>>> 11.10)
>>>>>>> ?
>>>>>
>>>>> No it doesn't violate structured locking as the number of enters
>>>>> and exits match, and there is always an enter before an exit.
>>>>>
>>>>>>> Actually, the words (every exit on a given monitor matches a
>>>>>>> preceding entry on that monitor) are not quite clear as for me.
>>>>>>> Otherwise the first rule (The number of monitor entries performed
>>>>>>> by T on M during a method invocation must equal the number of
>>>>>>> monitor exits performed by T on M during the method invocation
>>>>>>> whether the method  invocation completes normally or abruptly.)
>>>>>>> is sufficient.
>>>>>
>>>>> The number of enters and exits must not only match/balance, but
>>>>> there must be an enter before a corresponding exit.
>>>>>
>>>>>>>
>>>>>>> public class Search extends java.lang.Object {
>>>>>>>
>>>>>>> public void <init>()
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          r0 := @this: Search;
>>>>>>>          specialinvoke r0.<java.lang.Object: void <init>()>();
>>>>>>>          return;
>>>>>>>      }
>>>>>>>
>>>>>>> public void main(java.lang.String[]) throws java.lang.Exception
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          Search r2;
>>>>>>>          java.lang.String[] r1;
>>>>>>>          r0 := @this: Search;
>>>>>>>          r1 := @parameter0: java.lang.String[];
>>>>>>>          r2 = new Search;
>>>>>>>          specialinvoke r2.<Search: void <init>()>();
>>>>>>>          entermonitor r2;
>>>>>>>          entermonitor r0;
>>>>>>>          exitmonitor r2;
>>>>>>>          exitmonitor r0;
>>>>>>>          return;
>>>>>>>      }
>>>>>>> }
>>>>>>>
>>>>>>> (3) The next program enters monitor in <init> and exits it in main().
>>>>>>> HotSpot throws a runtime exception, while J9 does not. Should
>>>>>>> this program be rejected by the verifiers?
>>>>>
>>>>> No this does not violate any verification rules. The runtime
>>>>> behaviour depends on whether structured locking is enforced or
>>>>> not.(Even in hotspot there can be differences between interpreted and jitted code).
>>>>>
>>>>> Hope that helps clarify things.
>>>>>
>>>>> David
>>>>> -----
>>>>>
>>>>>>>
>>>>>>> public class Search extends java.lang.Object {
>>>>>>>
>>>>>>> public void <init>()
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          r0 := @this: Search;
>>>>>>>          specialinvoke r0.<java.lang.Object: void <init>()>();
>>>>>>>          entermonitor r0;
>>>>>>>          return;
>>>>>>>      }
>>>>>>>
>>>>>>> public void main(java.lang.String[]) throws java.lang.Exception
>>>>>>>      {
>>>>>>>          Search r0;
>>>>>>>          Search r2;
>>>>>>>          java.lang.S
> 


More information about the hotspot-runtime-dev mailing list