Note: this question is not related to volatile, AtomicLong or any perceived flaw in the described use case.
The property that I am trying to prove or exclude is as follows:
Given the following:
- recent 64-bit OpenJDK 7/8 (preferably 7, but 8 is also useful).
- Intel-base multiprocessor system
- non-volatile long primitive variable
- multiple unsynchronized mutator threads
- unsynchronized stream of observers
Is it always observed that the observer always encounters intact values ​​recorded by the mutator stream, or is this word breaking the danger?
JLS: Inconclusive
This property exists for 32-bit primitives and 64-bit object references, but JLS is not guaranteed for lengths and doubles:
17.7. Non-atomic processing of double and long:
For the purposes of the memory model of the Java programming language, one record in a non-volatile long or double value is considered as two separate records: one for each 32-bit half. This can lead to a situation where the stream sees the first 32 bits of a 64-bit value from one record and the second 32 bits from another record.
But hold the horses:
[...] For efficiency, this behavior is implementation-specific; An implementation of a Java virtual machine can write to long and double values ​​atomically or in two parts. Implementations of the Java virtual machine are recommended to avoid splitting 64-bit values ​​where possible. [...]
Thus, JLS allows JVM implementations to separate 64-bit entries and encourages developers to adjust them accordingly, but also encourages JVM developers to stick to 64-bit entries. We do not have an answer to the latest HotSpot versions yet.
HotSpot JIT: Careful Optimism
Since word tearing most likely occurs within the boundaries of hard loops and other hot spots, I tried to analyze the actual data collection from the JIT compilation. In short: further testing is necessary, but I can only see atomic 64-bit operations on longs.
I used hdis , a disassembler plugin for OpenJDK. Having created and installed the plugin in my old build of OpenJDK 7u25, I started writing a short program:
public class Counter { static long counter = 0; public static void main(String[] _) { for (long i = (long)1e12; i < (long)1e12 + 1e5; i++) put(i); System.out.println(counter); } static void put(long v) { counter += v; } }
I always used values ​​greater than MAX_INT (from 1e12 to 1e12 + 1e5) and repeated the operation enough times (1e5) to run JIT.
After compiling, I executed Counter.main () using hdis, for example:
java -XX:+UnlockDiagnosticVMOptions \ -XX:PrintAssemblyOptions=intel \ -XX:CompileCommand=print,Counter.put \ Counter
The assembly generated for Counter.put () JIT was as follows (decimal line numbers added for convenience):
01 # {method} 'put' '(J)V' in 'Counter' 02 ⇒ # parm0: rsi:rsi = long 03 # [sp+0x20] (sp of caller) 04 0x00007fdf61061800: sub rsp,0x18 05 0x00007fdf61061807: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry 06 ; - Counter:: put@-1 (line 15) 07 0x00007fdf6106180c: movabs r10,0x7d6655660 ; {oop(a 'java/lang/Class' = 'Counter')} 08 ⇒ 0x00007fdf61061816: add QWORD PTR [r10+0x70],rsi ;*putstatic counter 09 ; - Counter:: put@5 (line 15) 10 0x00007fdf6106181a: add rsp,0x10 11 0x00007fdf6106181e: pop rbp 12 0x00007fdf6106181f: test DWORD PTR [rip+0xbc297db],eax # 0x00007fdf6cc8b000 13 ; {poll_return}
Interesting lines are marked with "⇒". As you can see, the add operation is performed on a quad-core (64-bit) using 64-bit registers ( rsi ).
I also tried to find out if byte alignment is a problem by adding a padding variable for the byte immediately before the "long counter". The only difference in the assembly output:
front
0x00007fdf6106180c: movabs r10,0x7d6655660 ; {oop(a 'java/lang/Class' = 'Counter')}
after
0x00007fdf6106180c: movabs r10,0x7d6655668 ; {oop(a 'java/lang/Class' = 'Counter')}
Both addresses are 64-bit aligned, and calls to "movabs r10, ..." use 64-bit registers.
So far I have only tested the add-on. I guess subtraction behaves the same.
Other operations, such as bitwise operations, assignment, multiplication, etc., remains to be verified (or confirmed by someone familiar with the internal components of HotSpot).
Interpreter: Non-Convertible
This leaves us with a non-JIT script. Let decompile Compiler.class:
$ javap -c Counter [...] static void put(long); Code: 0: getstatic #8
... and we will be interested in the 'ladd' bytecode instruction in line # 7. However, I have not been able to trace it to before implementation on the platform so far.
Your help was appreciated!