Java – the relationship between bytecode instructions and processor operations
The Java specification guarantees that the original variable assignment is always atomic (long and double type expected)
On the contrary, the fetch and add operation, corresponding to the named I increment operation, will be non atomic because it leads to read modify write operations
Suppose this Code:
public void assign(int b) { int a = b; }
The generated bytecode is:
public void assign(int); Code: 0: iload_1 1: istore_2 2: return
Therefore, we see that allocation consists of two steps (load and store)
Suppose this Code:
public void assign(int b) { int i = b++; }
Bytecode:
public void assign(int); Code: 0: iload_1 1: iinc 1,1 //extra step here regarding the prevIoUs sample 4: istore_2 5: return
Know that x86 processors can (at least modern) operate incrementally as is:
Therefore, the first question: Although bytecode requires two steps (loading and storage), Java relies on the fact that allocation operations are always performed atomically, regardless of the processor architecture, so it can ensure that permanent atomicity (used for original allocation) is within its specification?
Second question: is it wrong to use a very modern x86 processor to confirm and not share compiled code between different architectures, so there is no need for synchronous I operation (or atomicinteger)? Considering that it is already atomic
Solution
Consider the second question
You mean I will translate the x86 fetch and add instruction, which is incorrect If the code is compiled and optimized by the JVM, it may be true (you must check the source code of the JVM to confirm), but the code can also run in interpretation mode, where fetch and add are separated and out of sync
Out of curiosity, I checked the assembly code generated for this java code:
public class Main { volatile int a; static public final void main (String[] args) throws Exception { new Main ().run (); } private void run () { for (int i = 0; i < 1000000; i++) { increase (); } } private void increase () { a++; } }
I used the Java hotspot (TM) server VM (17.0-b12-fastdebug) for windows x86 JRE (1.6.0_20-ea-fastdebug-b02) to build a JVM Version (I was somewhere on my drive) at 03:25:33 on April 1, 2010
These are the key outputs to run it (Java - server - XX: printassembly - cp.main):
At first it was compiled into this:
00c PUSHL EBP SUB ESP,8 # Create frame 013 MOV EBX,[ECX + #8] # int ! Field VolatileMain.a 016 MEMBAR-acquire ! (empty encoding) 016 MEMBAR-release ! (empty encoding) 016 INC EBX 017 MOV [ECX + #8],EBX ! Field VolatileMain.a 01a MEMBAR-volatile (unnecessary so empty encoding) 01a LOCK ADDL [ESP + #0],0 ! membar_volatile 01f ADD ESP,8 # Destroy frame POPL EBP TEST PollPage,EAX ! Poll Safepoint 029 RET
It is then inlined and compiled as follows:
0a8 B11: # B11 B12 <- B10 B11 Loop: B11-B11 inner stride: not constant post of N161 Freq: 0.999997 0a8 MOV EBX,[ESI] # int ! Field VolatileMain.a 0aa MEMBAR-acquire ! (empty encoding) 0aa MEMBAR-release ! (empty encoding) 0aa INC EDI 0ab INC EBX 0ac MOV [ESI],EBX ! Field VolatileMain.a 0ae MEMBAR-volatile (unnecessary so empty encoding) 0ae LOCK ADDL [ESP + #0],0 ! membar_volatile 0b3 CMP EDI,#1000000 0b9 Jl,s B11 # Loop end P=0.500000 C=126282.000000
You can see that it does not use the fetch and add instruction