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

David Holmes david.holmes at oracle.com
Thu May 18 04:57:03 UTC 2017


On 18/05/2017 2:18 PM, 陈雨亭 wrote:
> Hi, David,
>
> I still did not catch why it happens... I just read the specification and
> wrote some tests in order to understand some paragraphs better, and then had
> questions.
>
> Will the HotSpot's verifier detect such kind of uninitialized monitored
> objects in future?

I have filed a bug so this can be examined more closely.

https://bugs.openjdk.java.net/browse/JDK-8180581

Thanks,
David

> Regards,
> Yuting
>
> -----邮件原件-----
> 发件人: David Holmes [mailto:david.holmes at oracle.com]
> 发送时间: 2017年5月17日 20:23
> 收件人: 陈雨亭 <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
>
> 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.String[] r1;
>>>>         r0 := @this: Search;
>>>>         r1 := @parameter0: java.lang.String[];
>>>>         r2 = new Search;
>>>>         specialinvoke r2.<Search: void <init>()>();
>>>>         exitmonitor r2;
>>>>         exitmonitor r0;
>>>>         return;
>>>>     }
>>>> }
>>>>
>>>>
>>>
>


More information about the hotspot-runtime-dev mailing list