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 &lt;- 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

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>