字节码指令与处理器操作之间的关系 [英] Relation between bytecode instructions and processor operations
问题描述
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 <- 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屋!