Before talking about Java's memory model, it is easy to understand the concepts of main storage and cache of the computer.
Multitasking and high concurrency are important metrics for measuring a computer's processor. The average performance of a server is measured by the number of transactions per second (transactions per Second,tps), which represents the number of requests the server can respond to in a second, and the TPS value has a very close relationship with the program's concurrency capability. Since there are several orders of magnitude difference between the computer's storage device and the processor's computing power, modern computer systems have to add a layer of read-write speed that is as close to the processor speed as possible (cache) As a buffer between memory and processor: the data to be used to copy the operation to the cache, so that the operation can be done quickly, when the operation is finished and then back to memory from the cache, so that the processor does not have to wait for slow memory read and write. But each processor has its own cache of its own, so this leads to a problem: when multiple processors operate on the same variable in main memory at the same time, there is a cache inconsistency and a sequence of instruction execution problems, and JMM (Java memory model) is designed to solve these problems.
Concept
JMM is an abstract concept that does not exist in reality. It covers caching, write buffers, registers, and other hardware and compiler optimizations. In the JVM, thread-shared variables are stored in the heap, with each thread having its own private stack space, and information such as private variables is stored in stack memory. JMM is a language-level memory model that ensures that programmers are provided with persistent memory visibility on different compilers and processor platforms by prohibiting certain types of compilers from reordering and handling.
Memory Interaction Operations
The interaction of the memory defines how the working memory and main memory interact before, and the Java memory model defines eight operations altogether:
-
-
- Lock : acts on the main memory variable and identifies a variable as a thread exclusive state
- Unlock (Unlocked): acts on the main memory variable, releasing a variable that is locked, releasing the variable to be locked again by another thread
- Read : acting on the main memory variable, transferring a variable value from main memory to the working memory of the thread
- load (load): A variable that acts on the working memory, which puts the value of the variable fetched by the read operation from main memory into a variable copy of the working memory
- use: A variable acting on a working memory that passes the value of a variable in the working memory to the execution engine, which is executed whenever the virtual opportunity is to a bytecode instruction that needs to use a variable
- Assign (Assignment): A variable acting on a working memory that assigns a value received from the execution engine to a variable in the working memory, which will be performed whenever the virtual opportunity is assigned to a byte-code instruction that assigns a value to the variable.
- Store : A variable that acts on the working memory, transferring the value of a variable in the working memory to the main memory, using the subsequent write operations
- Write (write): acts on the main memory variable, which transfers the value of the store operation from the working memory to a variable in main memory
Re-order
From the Java source code to the final actual execution of the sequence of instructions, will go through the following three kinds of reordering:
-
- Compiler-Optimized reordering: The compiler can rearrange the execution order of a statement without changing the semantics of the single-threaded procedure
- Instruction-level parallel reordering: Modern processors use instruction-level parallelism (Instruction-level Parallelism, ILP) to overlap multiple instructions. If there is no data dependency, the processor can change the execution order of the statement corresponding to the machine instruction
- Memory system reordering: Because the processor uses cache and read/write buffers, this makes loading and storage operations appear to be in a disorderly sequence
Processor reordering and memory barrier directives
Modern processors use write buffers to temporarily save data written to memory. The write buffer guarantees continuous execution of the instruction pipeline, which avoids delays caused by the processor pausing to wait for the data to be written to the internal training. At the same time, you can reduce the memory bus footprint by flushing the write buffer in batches, and by combining multiple writes of the same memory address in the write buffer. While there are so many benefits to write buffers, the write buffers for each processor are only visible to the processor on which it resides. This feature has an important impact on the order in which memory operations are executed: the order in which the processor performs the read/write operations on the memory is not necessarily consistent with the actual read/write operation of the memory. Therefore, in order to ensure memory visibility, the Java compiler inserts a memory barrier directive in the appropriate place of the generated instruction sequence to suppress a particular type of handler reordering. JMM bar The memory barrier directive is divided into the following four categories:
| Barrier type |
instruction Example |
Description |
| Loadload barriers |
Load1; Loadload; Load2 |
Ensure the loading of the LOAD1 data, before the loading of the Load2 and all subsequent load instructions. |
| Storestore barriers |
Store1; Storestore; Store2 |
Ensure that the Store1 data is visible to other processors (flushed to memory), previously stored in Store2 and all subsequent storage instructions. |
| Loadstore barriers |
Load1; Loadstore; Store2 |
Ensure that the LOAD1 data is loaded before the Store2 and all subsequent storage instructions are flushed to memory. |
| Storeload barriers |
Store1; Storeload; Load2 |
Make sure that the Store1 data becomes visible to other processors (that is, flush to memory), before the loading of Load2 and all subsequent mount instructions. The Storeload barriers will make the memory access instruction after all memory access instructions (store and mount instructions) before the barrier is complete. |
Storeload barriers is a "all-in-one" barrier that simultaneously has the effect of three other barriers. Most modern processors support this barrier (other types of memory barrier directives are not necessarily supported by all processors). The overhead of executing the barrier is expensive, because the current processor usually wants to write the buffer data to the memory.
Sequential consistency data competition and sequential consistency guarantee
When the program is not synchronizing correctly, there may be data contention. The Java memory model defines the competition for data as follows:
-
-
-
- Write a variable in a thread
- Read the same variable on another thread
- and read and write are not sorted by synchronization
When the code contains data competition, the execution of the program often produces counterintuitive results. If a multithreaded program can be properly synchronized, this program will be a non-data competition, JMM for the correct synchronization of the
The memory consistency of the program makes the following guarantees:
-
-
-
- If the program is synchronized correctly, the execution of the program will be sequential (sequentially consistent)-that is, the execution result of the program is the same as that of the program in the sequential consistent memory model. The synchronization referred to here is a generalized synchronization, including the correct use of common synchronization primitives (synchronized, volatile, final)
Sequential consistent memory model
Sequential consistent memory model is a theoretical reference model idealized by computer scientists, which provides a very strong guarantee of memory visibility for programmers. Sequential consistent memory models have a large number of features:
-
-
-
- All operations in a thread must be executed in the order of the program
- (regardless of whether the program is synchronized) all threads can see only a single order of operation execution. In the sequential consistent memory model, each operation must be atomic and immediately visible to all threads
Execution characteristics of an unsynchronized program
For programs that are not synchronized or are not synchronized correctly, JMM provides only minimal security: the value that the thread executes to read, either the value written by a previous thread, or the default value (0,false,null). JMM guarantees that the read operation reads a value that does not come out of nowhere (out of thin air). For minimum security, when JMM allocates memory on the heap, it will first clear 0 of the memory space before allocating the object (both operations are synchronized internally by the JVM). Therefore, the default initialization of the domain is completed when the object is allocated in the Zeroed memory space (pre-zeroed memories).
JMM does not guarantee that the execution result of the unsynchronized program is consistent with the execution result of the program in the sequential consistency model. Because to ensure consistent execution results, JMM needs to suppress a large number of processor and compiler optimizations, which can have a significant impact on the execution performance of the program. And when the unsynchronized program executes in the sequential consistency model, the whole is unordered, and its execution results are often unpredictable. So it makes no sense to ensure that the results of an unsynchronized program are consistent between the two models.
When an unsynchronized program executes in JMM, it is unordered on the whole and its execution is unpredictable. There are several differences in the execution characteristics of an unsynchronized program in two models:
-
-
- The sequential consistency model guarantees that operations within a single thread are performed in the order of the program, while JMM does not guarantee that operations within a single thread will be executed in the order of the program
- The sequential consistency model guarantees that all threads see only a consistent sequence of operations execution, while JMM does not guarantee that all threads will see a consistent sequence of operations
- JMM does not guarantee that read/write operations on 64-bit long/double variables are atomic, and the sequential consistency model guarantees atomicity for all memory read/write operations
Reference:<< in-depth understanding of Java memory Models >> Cheng
Java memory model (JMM)