This is the 28th day of my participation in Gwen Challenge. Following on from the previous post hello, what about the volatile keyword? (3)
4.5 Memory Barriers
As mentioned above, instruction reordering can cause visibility problems. The solution is to use memory barriers. What is a memory barrier?
A memory barrier is a set of processor instructions (hardware dependent) that impose restrictions on the order in which memory operations are performed. It can be used to achieve visibility of multiple threads accessing the same resource in memory.
4.5.1 Analyzing memory barriers using the Hsdis tool
Hsdis is a disassembly library for analyzing code generated by the JIT compiler at Java runtime. It is a DLL (dynamic link library) file that is used in the ${JAVA_HOME}/jre/bin/server directory. We used this tool to compare the difference between an assembly instruction that prefixes a variable with a volatile keyword and one that does not.
You start by configuring the Hsdis environment in the Java installation directory, which essentially puts the two files in the specified directory.
After configuring the Hsdis environment, add the following parameters to the VM Options where you run the program.
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileExample.*
Copy the code
-XX:+UnlockDiagnosticVMOptions
Parameter indicates that diagnostic mode is enabled for printingPrintAssembly
Kind of information.-XX:+PrintAssembly
The parameter stands for printing just-in-time compiled binary informationXX:CompileCommand=compileonly,*VolatileExample.*
This indicates that only information that satisfies the *VolatileExample.* regular expression is displayed.
Then we run the VolatileExample example in Chapter 2.
The volatile run assembly instruction was found to be
0x0000000003565073: lock add dword ptr [rsp],0h ; *putstatic runCopy the code
The unvolatile run assembly instruction is
0x00000000030e5ceb: push 0ffffffffc4834801h ; *putstatic runCopy the code
You can clearly see the difference between volatile variables and a lock instruction. Lock is an assembly control instruction that implements a memory barrier.
4.5.2 Detail the memory barrier
In the Case of the Java language, memory barriers are not directly exposed for use by the JVM, but are inserted by the JVM into the underlying runtime instructions based on code semantics. For example, if volatile is used to modify the variable, the assembly instruction will add the LOCK instruction.
The memory barrier instructions are different for different hardware exposed to the outside world. The familiar X86 computer device, for example, provides Ifence(read barrier), Sfence(write barrier), and Mfence (full fence) instructions to enforce the memory barrier.
- A Load Memory Barrier is all read operations that are performed behind a read Barrier. This instruction is done in conjunction with the write barrier so that the memory updates before the write barrier and the read operations after the read barrier are visible. In essence, all the invalidation instructions already in the invalidation queue are applied before the barrier is read.
- A Store Memory Barrier means that all writes that precede a write Barrier must be created by
Store Buffer
Synchronously flush to main memory. The effect is that all read and write operations after the write barrier see the memory update before the write barrier. - A Full Memory Barrier is a Barrier that allows all read and write operations before it to be visible to all read and write operations after it.
So for the example in Section 4.4.3, let’s make a simple change and add a memory barrier for visibility.
int value =1; bool finish = false; void runOnCPU1(){ value = 2; storeMemoryBarrier(); // Write barriers to force variables to be flushed to main memory finish = true; } void runOnCPU2(){ if(finish){ loadMemoryBarrier(); Assert == 2; assert == 2; }}Copy the code
In summary, the Java compiler solves the instruction reordering problem by inserting memory barriers before and after volatile reads and writes. In particular, for the Java language, the real hero of instruction reordering is the JMM(Java Memory Model). As a bridge to the underlying implementation of complex hardware, the JMM addresses visibility and ordering issues by providing reasonable methods to disable caching and reordering, which are then translated into specific CPU instructions at compile time, including synchronized, volatile, and final. This chapter will not be in-depth, and detailed analysis will be carried out later. Hello, what about the keyword volatile? (5)
Brother boy, don’t panic to go! Leave a thumbs-up and comment on the discussion. Welcome to the column face interview don’t panic | Java concurrent programming, a raise don’t have to worry about the interview. Also welcome to pay attention to me, must be a longer better man.