字节码指令与处理器操作之间的关系 [英] Relation between bytecode instructions and processor operations

查看:254
本文介绍了字节码指令与处理器操作之间的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java规范保证原始变量赋值始终是原子的(期望 long 和double types

Java specification guarantees primitive variable assignments are always atomic (expect for long and double types.

相反, Fetch-and-Add 与着名的 i ++ 增量操作相对应的操作将是非原子操作,因为这会导致读 - 修改 - 写操作。

On the contrary, Fetch-and-Add operation corresponding to the famous i++ increment operation, would be non-atomic because leading to a read-modify-write operation.

假设这段代码:

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

生成的字节码为:

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

因此,我们看到作业由两个步骤组成(加载和存储) 。

Thus, we see the assignment is composed of two steps (loading and storing).

假设此代码:

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处理器(至少是现代处理器)可以原子方式操作增量操作,如上所述:

Knowing that X86 processor can (at least modern ones), operates increment operation atomically, as said:


在计算机科学中,fetch-and-add CPU指令是一个特殊的
指令,它以原子方式修改内存
位置的内容。它用于在多处理器系统中实现互斥和并发
算法,这是信号量的推广。

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依赖于这样一个事实,即分配操作总是以原子方式执行,无论处理器的体系结构如何,因此可以确保永久的原子性(对于原始作业)在其规范中?

Thus, first question: Despite of the fact that bytecode requires both steps (loading and storage), does Java rely on the fact that assignment operation is an operation always carried out atomically whatever the processor's architecture and so can ensure permanent atomicity (for primitive assignments) in its specification?

第二个问题:用非常现代的X86处理器确认并且不在不同架构之间共享编译代码是不对的,根本不需要同步 i ++ 操作(或 AtomicInteger )?考虑到它已经是原子的。

Second question: Is it wrong to confirm that with very modern X86 processor and without sharing compiled code across different architectures, there's no need at all to synchronize the i++ operation (or AtomicInteger)? Considering it already atomic.

推荐答案

考虑第二个问题

你暗示 i ++ 将转换为X86 Fetch-And-Add指令,但这不是真的。如果代码是由JVM编译和优化的,那么可能为真(必须检查JVM的源代码以确认),但该代码也可以在解释模式下运行,其中 fetch add 是分离的而不是同步的。

You imply that i++ will translate into the X86 Fetch-And-Add instruction which is not true. If the code is compiled and optimized by the JVM it may be true (would have to check the source code of JVM to confirm that), but that code can also run in interpreted mode, where the fetch and add are seperated and not synchronized.

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

Out of curiosity I checked what assembly code is 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++;
  }
}

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

I used Java HotSpot(TM) Server VM (17.0-b12-fastdebug) for windows-x86 JRE (1.6.0_20-ea-fastdebug-b02), built on Apr 1 2010 03:25:33 version of JVM (this one I had somewhere on my drive).

这是运行它的关键输出( java -server -XX:+ PrintAssembly -cp。主):

These is the crucial output of running it (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

然后将其内联并编译成:

Then it is inlined and compiled into this:

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

正如您所看到的,它不使用 a ++ 的获取和添加指令。

As you can see it does not use Fetch-And-Add instructions for a++.

这篇关于字节码指令与处理器操作之间的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆