加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 字节码指令和处理器操作之间的关系

发布时间:2020-12-14 17:47:43 所属栏目:Java 来源:网络整理
导读:Java规范保证原始变量赋值始终是原子的(期望长和双类型. 相反,Fetch-and-Add操作对应着名的i增量操作,将是非原子的,因为导致了读 – 修改 – 写操作. 假设这个代码: public void assign(int b) { int a = b;} 生成的字节码是: public void assign(int); Co
Java规范保证原始变量赋值始终是原子的(期望长和双类型.

相反,Fetch-and-Add操作对应着名的i增量操作,将是非原子的,因为导致了读 – 修改 – 写操作.

假设这个代码:

public void assign(int b) {
    int a = b;
}

生成的字节码是:

public void assign(int);
    Code:
       0: iload_1       
       1: istore_2      
       2: return

因此,我们看到分配由两个步骤组成(加载和存储).

假设这个代码:

public void assign(int b) {
        int i = b++;
}

字节码:

public void assign(int);
    Code:
       0: iload_1       
       1: iinc          1,1    //extra step here regarding the previous sample
       4: istore_2      
       5: return

知道X86处理器可以(至少是现代的)可以按原样操作增量操作:

In computer science,the fetch-and-add CPU instruction is a special
instruction that atomically modifies the contents of a memory
location. It is used to implement mutual exclusion and concurrent
algorithms in multiprocessor systems,a generalization of semaphores.

因此,第一个问题:尽管字节码需要两个步骤(加载和存储),但是Java依赖于这样一个事实,即分配操作是始终以原子方式执行的操作,无论处理器的架构如何,因此可以确保永久原子性(用于原始分配)在其规格?

第二个问题:使用非常现代化的X86处理器来确认并且在不同体系结构之间没有共享编译代码是错误的,所以不需要同步i操作(或AtomicInteger)?考虑到它已经是原子的.

解决方法

考虑第二个问题.

你意味着我会翻译成X86 Fetch-And-Add指令,这是不正确的.如果代码由JVM进行编译和优化,则可能是真的(必须检查JVM的源代码来确认),但是该代码也可以在解释模式下运行,其中fetch和add被分离并且不同步.

出于好奇,我检查了为这个Java代码生成的汇编代码:

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++;
  }
}

我使用Java HotSpot(TM)Server VM(17.0-b12-fastdebug)for windows-x86 JRE(1.6.0_20-ea-fastdebug-b02),建立于2010年4月1日03:25:33版本的JVM(这一个我在我的驱动器的某个地方).

这些是运行它的关键输出(java -server -XX:PrintAssembly -cp.Main):

起初它被编成这个:

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

然后它被内联和编译成这样:

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

你可以看到它不使用Fetch-And-Add指令.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读