Analyzing the underlying principle of JMM and volatile through examples

Time:2020-9-13

This article mainly introduces the bottom principle of JMM and volatile through examples. The introduction of the example code is very detailed, which has certain reference learning value for everyone’s study or work. Friends in need can refer to it

JMM and volatile analysis

1. JMM: Java Memory Model, java thread memory model

JMM: it is an abstract concept, which describes the communication between threads and memory. Java thread memory model is similar to CPU cache model. It is standardized and is used to shield the difference of memory access between hardware and operating system.

2. JMM combined with 8 atomic operations

3. Application of volatile and its underlying principles

Volatile: lightweight synchronized, which ensures the “visibility” of shared variables in multiprocessor development. Visibility means: when a thread modifies a shared variable, other threads that use the shared variable can read the modified value in time. Properly decorated, it has lower execution cost than synchronized because it does not cause thread context switching and scheduling.

public class VolatileTest {
  private static volatile boolean flag = false;
  public static void main(String[] args) {
    update();
  }

  public static void update(){
    flag = true;
    System.out.println(flag);
  }
}
The volatile JIT compiler compiles java code to view for assembly instructions
1. Add hsdis-amd64.lib in the JDK / JRE / bin directory
2. Add the hsdis-amd64.dll file in the JDK1.8 / JRE / bin / server directory
3. Set JVM parameters in idea
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,VolatileTest.update
4. Run Java program to print it out
CompilerOracle: compileonly *VolatileTest.update
Loaded disassembler from E:\EclipseDev\jdk\jdk1.8\jre\bin\server\hsdis-amd64.dll
Decoding compiled method 0x000000000f11aad0:
Code:
Argument 0 is unknown.RIP: 0xf11ac40 Code size: 0x000002a8
[Disassembling for mach='amd64']
[Entry Point]
[Verified Entry Point]
[Constants]
 # {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest'
 #      [sp+0x40] (sp of caller)
 0x000000000f11ac40: mov   dword ptr [rsp+0ffffffffffffa000h],eax
 0x000000000f11ac47: push  rbp
 0x000000000f11ac48: sub   rsp,30h
 0x000000000f11ac4c: mov   r8,8792d70h    ;  {metadata(method data for {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')}
 0x000000000f11ac56: mov   edx,dword ptr [r8+0dch]
 0x000000000f11ac5d: add   edx,8h
 0x000000000f11ac60: mov   dword ptr [r8+0dch],edx
 0x000000000f11ac67: mov   r8,8792b70h    ;  {metadata({method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')}
 0x000000000f11ac71: and   edx,0h
 0x000000000f11ac74: cmp   edx,0h
 0x000000000f11ac77: je   0f11ad68h     ;*iconst_1
                        ; - com.yew.test.VolatileTest::[email protected] (line 17)
 0x000000000f11ac7d: mov   r8,0d7b08a30h   ;  {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')}
 0x000000000f11ac87: mov   edx,1h
 0x000000000f11ac8c: mov   byte ptr [r8+68h],dl
Volatile modification
 0x000000000f11ac90: lock add dword ptr [rsp],0h ;*putstatic flag
                        ; - com.yew.test.VolatileTest::[email protected] (line 17)
No volatile modification
 0x000000000f113707: mov byte ptr [r8+68h],1h ;*putstatic flag
                        ; - com.yew.test.VolatileTest::[email protected] (line 17)
Through comparison, it can be seen that changing the value of shared variable flag to true is modified by volatile. When assembly printing, there will be lock prefix modification. According to IA-32 architecture software developer manual, lock prefix instruction will cause two things under multi-core CPU processor
【1】 Writes the data of the current processor cache row back to system memory immediately
【2】 The wire operation will invalidate data cached at this memory address in other processors
During lock ා assertion, the processor monopolizes any shared memory. IA-32 processor and Intel 64 processor use MESI (modify, exclusive, share, invalid) control protocol to maintain the consistency of internal cache and other processor cache. Through sniffing technology, the data stored in processor internal cache, system cache and other processors are consistent on the bus. When other processors intend to write back the memory address, which is the shared memory area, the sniffer processor will set its cache line to invalid, and force the cache line filling when accessing the same memory the next time.
0x000000000f11ac95: nop
 0x000000000f11ac98: jmp   0f11add4h     ;  {no_reloc}
 0x000000000f11ac9d: add   byte ptr [rax],al
 0x000000000f11ac9f: add   byte ptr [rax],al
 0x000000000f11aca1: add   byte ptr [rsi+0fh],ah
 0x000000000f11aca4: Fatal error: Disassembling failed with error code: 15Decoding compiled method 0x000000000f11ef50:
Code:
Argument 0 is unknown.RIP: 0xf11f080 Code size: 0x00000058
[Entry Point]
[Verified Entry Point]
[Constants]
 # {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest'
 #      [sp+0x20] (sp of caller)
 0x000000000f11f080: mov   dword ptr [rsp+0ffffffffffffa000h],eax
 0x000000000f11f087: push  rbp
 0x000000000f11f088: sub   rsp,10h
 0x000000000f11f08c: mov   r10,0d7b08a30h  ;  {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')}
 0x000000000f11f096: mov   byte ptr [r10+68h],1h
 0x000000000f11f09b: lock add dword ptr [rsp],0h ;*putstatic flag
                        ; - com.yew.test.VolatileTest::[email protected] (line 17)
 0x000000000f11f0a0: mov   edx,1ch
 0x000000000f11f0a5: nop
 0x000000000f11f0a7: call  0f0557a0h     ; OopMap{off=44}
                        ;*getstatic out
                        ; - com.yew.test.VolatileTest::[email protected] (line 18)
                        ;  {runtime_call}
 0x000000000f11f0ac: int3           ;*getstatic out
                        ; - com.yew.test.VolatileTest::[email protected] (line 18)
 0x000000000f11f0ad: hlt
 0x000000000f11f0ae: hlt
 0x000000000f11f0af: hlt
 0x000000000f11f0b0: hlt
 0x000000000f11f0b1: hlt
 0x000000000f11f0b2: hlt
 0x000000000f11f0b3: hlt
 0x000000000f11f0b4: hlt
 0x000000000f11f0b5: hlt
 0x000000000f11f0b6: hlt
 0x000000000f11f0b7: hlt
 0x000000000f11f0b8: hlt
 0x000000000f11f0b9: hlt
 0x000000000f11f0ba: hlt
 0x000000000f11f0bb: hlt
 0x000000000f11f0bc: hlt
 0x000000000f11f0bd: hlt
 0x000000000f11f0be: hlt
 0x000000000f11f0bf: hlt
[Exception Handler]
[Stub Code]
 0x000000000f11f0c0: jmp   0f0883a0h     ;  {no_reloc}
[Deopt Handler Code]
 0x000000000f11f0c5: call  0f11f0cah
 0x000000000f11f0ca: sub   qword ptr [rsp],5h
 0x000000000f11f0cf: jmp   0f057600h     ;  {runtime_call}
 0x000000000f11f0d4: hlt
 0x000000000f11f0d5: hlt
 0x000000000f11f0d6: hlt
 0x000000000f11f0d7: hlt
true

4. Optimization of volatile

Doug Li, a Java concurrency guru, added a new queue set linketransferqueue in JDK7 package. When modifying variables with volatile keyword, it fills variables to 64 bytes by appending bytes

When a volatile modifier variable is modified, it will lock the pre instruction to lock the data exclusive of the cache row

Applicable to: 64 byte cache line byte processor, such as i7 core Pentium M, etc

Not applicable: non 64 byte wide cache lines P6 series or Pentium shared variables will not be frequently written

5. Three characteristics of concurrent programming: visibility, atomicity and orderliness

Volatile can guarantee visibility and order, but it doesn’t guarantee atomicity.

6. Semantic analysis of volatile keyword

(1) To ensure visibility, when volatile decorated shared variables are modified, other processors can immediately sniff the change of shared variable values

(2) Ensure order: according to the happens before principle, when variables are decorated with volatile, the position before and after the program code cannot be rearranged and extracted.

(3) Volatile uses assembly lock prefix instruction to lock the cache line of shared variable memory address to control concurrency security (lightweight synchronized)

7. Usage scenarios of volatile and the difference between volatile and synchronized

Usage scenarios: 1. Flag state; 2. DCL — double detection lock (single instance mode); 3. Ensure visibility and sequence

difference:

1. Usage: volatile modifier variable synchronized decorated method or code block

2. Atomicity is not guaranteed. Synchronized can guarantee atomicity

3. The visibility guarantee mechanism is different. Volatile uses the monitor attribute (moniterenternet entry moniterexit — exit (including exception)) through the lock prefix instruction of assembly

4. The granularity of lock guaranteed by order is smaller than that of synchronized

5. Other volatile will not cause thread blocking, while synchronized will cause thread blocking

The above is the whole content of this article, I hope to help you in your study, and I hope you can support developeppaer more.