My java code is as follows
public class MyClass {
volatile int voltile ; //7
int nonVoltile ; //8
public static void main(String[] args) {
for(int i=1; i<100000; i++){
f();
}
}
static void f(){ //16
MyClass t = new MyClass(); //17
t.voltile = t.nonVoltile; //18
t.nonVoltile = 0x11111; //20
t.voltile = 0x22222; //21
t.nonVoltile = t.nonVoltile + 1; //23
t.voltile = t.voltile + 1; //24
}
}
Generated assembly snippets for function "f" is as follows
For volatile write
0x024c73ff: mov 0xc(%esi),%eax
0x024c7402: mov %eax,0x8(%esi)
0x024c7405: lock addl $0x0,(%esp) ;*putfield voltile
; - j.assembly.MyClass::f@13 (line 18)
I have following questions
For volatile read
0x024c7425: mov 0x8(%esi),%eax ;*getfield voltile
; - j.assembly.MyClass::f@40 (line 24)
Only a single read instruction is generated
As asked I am also pasting the complete assembly
CompilerOracle: print *MyClass.f
Compiled method (c1) 142 14 ! j.assembly.MyClass::f (81 bytes)
total in heap [0x024c72c8,0x024c77d8] = 1296
relocation [0x024c7398,0x024c73bc] = 36
main code [0x024c73c0,0x024c7600] = 576
stub code [0x024c7600,0x024c7620] = 32
oops [0x024c7620,0x024c7624] = 4
metadata [0x024c7624,0x024c7628] = 4
scopes data [0x024c7628,0x024c76a4] = 124
scopes pcs [0x024c76a4,0x024c77d4] = 304
dependencies [0x024c77d4,0x024c77d8] = 4
Loaded disassembler from hsdis-i386.dll
Decoding compiled method 0x024c72c8:
Code:
[Disassembling for mach='i386']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x14830304} 'f' '()V' in 'j/assembly/MyClass'
# [sp+0x40] (sp of caller)
0x024c73c0: mov %eax,0xffffc000(%esp)
0x024c73c7: push %ebp
0x024c73c8: sub $0x38,%esp
0x024c73cb: mov $0x14830350,%edx ; {metadata('j/assembly/MyClass')}
0x024c73d0: mov %fs:0x0,%ecx
0x024c73d8: mov 0xfffffff4(%ecx),%ecx
0x024c73db: mov 0x34(%ecx),%eax
0x024c73de: lea 0x10(%eax),%edi
0x024c73e1: cmp 0x3c(%ecx),%edi
0x024c73e4: ja 0x024c7589
0x024c73ea: mov %edi,0x34(%ecx)
0x024c73ed: mov 0x60(%edx),%ecx
0x024c73f0: mov %ecx,(%eax)
0x024c73f2: mov %edx,0x4(%eax)
0x024c73f5: xor %ecx,%ecx
0x024c73f7: mov %ecx,0x8(%eax)
0x024c73fa: mov %ecx,0xc(%eax)
0x024c73fd: mov %eax,%esi ;*new ; - j.assembly.MyClass::f@0 (line 17)
0x024c73ff: mov 0xc(%esi),%eax
0x024c7402: mov %eax,0x8(%esi)
0x024c7405: lock addl $0x0,(%esp) ;*putfield voltile
; - j.assembly.MyClass::f@13 (line 18)
0x024c740a: movl $0x11111,0xc(%esi) ;*putfield nonVoltile
; - j.assembly.MyClass::f@19 (line 20)
0x024c7411: mov $0x22222,%eax
0x024c7416: mov %eax,0x8(%esi)
0x024c7419: lock addl $0x0,(%esp) ;*putfield voltile
; - j.assembly.MyClass::f@25 (line 21)
0x024c741e: movl $0x11112,0xc(%esi) ;*putfield nonVoltile
; - j.assembly.MyClass::f@35 (line 23)
0x024c7425: mov 0x8(%esi),%eax ;*getfield voltile
; - j.assembly.MyClass::f@40 (line 24)
0x024c7428: inc %eax
0x024c7429: mov %eax,0x8(%esi)
0x024c742c: lock addl $0x0,(%esp) ;*putfield voltile
; - j.assembly.MyClass::f@45 (line 24)
0x024c7431: lea 0x20(%esp),%edi
0x024c7435: mov %esi,0x4(%edi)
0x024c7438: mov (%esi),%eax
0x024c743a: mov %eax,%ebx
0x024c743c: and $0x7,%ebx
0x024c743f: cmp $0x5,%ebx
0x024c7442: jne 0x024c74ca
0x024c7448: mov %eax,(%edi)
0x024c744a: mov 0x4(%esi),%ebx
0x024c744d: mov 0x60(%ebx),%ebx
0x024c7450: xor %eax,%ebx
0x024c7452: mov %fs:0x0,%eax
0x024c745a: mov 0xfffffff4(%eax),%eax
0x024c745d: xor %ebx,%eax
0x024c745f: and $0xffffff87,%eax
0x024c7462: je 0x024c74eb
0x024c7468: test $0x7,%eax
0x024c746d: jne 0x024c74be
0x024c746f: test $0x180,%eax
0x024c7474: jne 0x024c749a
0x024c7476: mov (%edi),%eax
0x024c7478: and $0x1ff,%eax
0x024c747e: mov %fs:0x0,%ebx
0x024c7486: mov 0xfffffff4(%ebx),%ebx
0x024c7489: or %eax,%ebx
0x024c748b: lock cmpxchg %ebx,(%esi)
0x024c748f: jne 0x024c7595
0x024c7495: jmp 0x024c74eb
0x024c749a: mov 0x4(%esi),%ebx
0x024c749d: mov 0x60(%ebx),%ebx
0x024c74a0: mov %fs:0x0,%eax
0x024c74a8: mov 0xfffffff4(%eax),%eax
0x024c74ab: or %eax,%ebx
0x024c74ad: mov (%edi),%eax
0x024c74af: lock cmpxchg %ebx,(%esi)
0x024c74b3: jne 0x024c7595
0x024c74b9: jmp 0x024c74eb
0x024c74be: mov (%edi),%eax
0x024c74c0: mov 0x4(%esi),%ebx
0x024c74c3: mov 0x60(%ebx),%ebx
0x024c74c6: lock cmpxchg %ebx,(%esi)
0x024c74ca: mov (%esi),%eax
0x024c74cc: or $0x1,%eax
0x024c74cf: mov %eax,(%edi)
0x024c74d1: lock cmpxchg %edi,(%esi)
0x024c74d5: je 0x024c74eb
0x024c74db: sub %esp,%eax
0x024c74dd: and $0xfffff003,%eax
0x024c74e3: mov %eax,(%edi)
0x024c74e5: jne 0x024c7595 ;*monitorenter
; - j.assembly.MyClass::f@51 (line 26)
0x024c74eb: mov 0xc(%esi),%eax ;*getfield nonVoltile
; - j.assembly.MyClass::f@54 (line 27)
0x024c74ee: inc %eax
0x024c74ef: mov %eax,0xc(%esi) ;*putfield nonVoltile
; - j.assembly.MyClass::f@59 (line 27)
0x024c74f2: mov 0x8(%esi),%eax ;*getfield voltile
; - j.assembly.MyClass::f@64 (line 28)
0x024c74f5: inc %eax
0x024c74f6: mov %eax,0x8(%esi)
0x024c74f9: lock addl $0x0,(%esp) ;*putfield voltile
; - j.assembly.MyClass::f@69 (line 28)
0x024c74fe: lea 0x20(%esp),%eax
0x024c7502: mov 0x4(%eax),%edi
0x024c7505: mov (%edi),%esi
0x024c7507: and $0x7,%esi
0x024c750a: cmp $0x5,%esi
0x024c750d: je 0x024c7527
0x024c7513: mov (%eax),%esi
0x024c7515: test %esi,%esi
0x024c7517: je 0x024c7527
0x024c751d: lock cmpxchg %esi,(%edi)
0x024c7521: jne 0x024c75a6 ;*monitorexit
; - j.assembly.MyClass::f@73 (line 26)
0x024c7527: add $0x38,%esp
0x024c752a: pop %ebp
0x024c752b: test %eax,0xdd0100 ; {poll_return}
0x024c7531: ret ;*return
; - j.assembly.MyClass::f@80 (line 30)
0x024c7532: mov %fs:0x0,%esi
0x024c753a: mov 0xfffffff4(%esi),%esi
0x024c753d: mov 0x1a4(%esi),%eax
0x024c7543: movl $0x0,0x1a4(%esi)
0x024c754d: movl $0x0,0x1a8(%esi)
0x024c7557: mov %eax,%esi
0x024c7559: lea 0x20(%esp),%eax
0x024c755d: mov 0x4(%eax),%ebx
0x024c7560: mov (%ebx),%edi
0x024c7562: and $0x7,%edi
0x024c7565: cmp $0x5,%edi
0x024c7568: je 0x024c7582
0x024c756e: mov (%eax),%edi
0x024c7570: test %edi,%edi
0x024c7572: je 0x024c7582
0x024c7578: lock cmpxchg %edi,(%ebx)
0x024c757c: jne 0x024c75b7 ;*monitorexit
; - j.assembly.MyClass::f@78 (line 26)
0x024c7582: mov %esi,%eax
0x024c7584: jmp 0x024c75ec
0x024c7589: mov %edx,%edx
0x024c758b: call 0x024bc740 ; OopMap{off=464}
;*new ; - j.assembly.MyClass::f@0 (line 17)
; {runtime_call}
0x024c7590: jmp 0x024c73fd
0x024c7595: mov %esi,0x4(%esp)
0x024c7599: mov %edi,(%esp)
0x024c759c: call 0x024bdc40 ; OopMap{esi=Oop [36]=Oop off=481}
;*monitorenter
; - j.assembly.MyClass::f@51 (line 26)
; {runtime_call}
0x024c75a1: jmp 0x024c74eb
0x024c75a6: lea 0x20(%esp),%eax
0x024c75aa: mov %eax,(%esp)
0x024c75ad: call 0x024bde00 ; {runtime_call}
0x024c75b2: jmp 0x024c7527
0x024c75b7: lea 0x20(%esp),%eax
0x024c75bb: mov %eax,(%esp)
0x024c75be: call 0x024bde00 ; {runtime_call}
0x024c75c3: jmp 0x024c7582
0x024c75c5: nop
0x024c75c6: nop
0x024c75c7: mov %fs:0x0,%esi
0x024c75cf: mov 0xfffffff4(%esi),%esi
0x024c75d2: mov 0x1a4(%esi),%eax
0x024c75d8: movl $0x0,0x1a4(%esi)
0x024c75e2: movl $0x0,0x1a8(%esi)
0x024c75ec: add $0x38,%esp
0x024c75ef: pop %ebp
0x024c75f0: jmp 0x024bbec0 ; {runtime_call}
0x024c75f5: hlt
0x024c75f6: hlt
0x024c75f7: hlt
0x024c75f8: hlt
0x024c75f9: hlt
0x024c75fa: hlt
0x024c75fb: hlt
0x024c75fc: hlt
0x024c75fd: hlt
0x024c75fe: hlt
0x024c75ff: hlt
[Exception Handler]
[Stub Code]
0x024c7600: call 0x024bd6c0 ; {no_reloc}
0x024c7605: push $0x77f387fc ; {external_word}
0x024c760a: call 0x024c760f
0x024c760f: pusha
0x024c7610: call 0x77e22130 ; {runtime_call}
0x024c7615: hlt
[Deopt Handler Code]
0x024c7616: push $0x24c7616 ; {section_word}
0x024c761b: jmp 0x0245c2b0 ; {runtime_call}
OopMapSet contains 2 OopMaps
#0
OopMap{off=464}
#1
OopMap{esi=Oop [36]=Oop off=481}
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true
Java HotSpot(TM) Client VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
What you see in the generated disassembly is a peculiarity of Java volatile
fields.
The topic cannot be explained briefly because it arises from the the way CPUs evolved and works, but simply put, given the instructions:
THREAD 0 THREAD 1
int x = 0, y = 0; while (y==0);
x = 1; int z = x, w = y;
y = x + 1;
THREAD 1 can very well ends with z=0
and w=2
.
This is not what you generally want. To better understand why this happen refer to this great blog or this nice text.
To address this problem Java define a Memory Model, you can read more about it in the chapter 17.4 of Java Language Specification.
In that model it is defined a relationship between instructions called happen-before and such relationship ensures that every side-effect of an instruction that happen-before another instruction is visible to the latter.
Simply put this guarantees that you have no nasty surprise like in the example above, where, being in two different thread, the instructions x = 1
and int z = x
are not in an happen-before relationship and there is no guarantee that the write to x
is visible to another thread (there is however the guarantee that it is visible to THREAD 0).
The chapter 17.4 lists what makes two instructions in a happen-before relationship, for example being in the same thread.
Another condition that make two instructions in such a relationship is being into another kind of relationship: the synchronize-with relationship.
Java volatile
field generate a synchronize-with between a write to and a read from that field. This is a very strong condition!
Simply put: volatile
-> synchronize-with -> happen-before -> serialization of side effects.
So Java volatile
is a mechanism of synchronization that can be often used instead of a lock.
In order to satisfy the happen-before relationship between threads it is necessary to use a memory barrier.
You compiled your Java code for IA32 architecture, a.k.a. x86. There are various fence instructions in such architecture that are dedicated to fine tuning. And there are raw serializing instructions: these are instructions that guarantee that every other instruction before, including their side effects, are completed before the serializing one is completed.
They are the barriers of the poor man.
lock addl $0x0,(%esp)
As you can see this instruction do nothing, it adds zero to the DWORD at esp
, but it does it using the lock
prefix, which is serializing, therefore acting as a memory barrier. An heavy one to be honest.
The other questions of your are simply to answer:
nonVoltile
(first move) and writing to voltile
(second move).voltile
serializes both write and read operations.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With