Home >Java >javaTutorial >In-depth analysis of Java memory model: summary
Processor Memory Model
The sequential consistency memory model is a theoretical reference model. The sequential consistency memory model is usually used as a reference when designing JMM and processor memory models. The JMM and processor memory model will make some relaxations in the sequential consistency model when designing, because if the processor and JMM are implemented completely in accordance with the sequential consistency model, many processor and compiler optimizations will be prohibited, which is detrimental to Execution performance will have a big impact.
According to the relaxation of the execution order of different types of read/write operation combinations, the memory models of common processors can be divided into the following types:
Relax the order of write-read operations in the program, by This resulted in the total store ordering memory model (TSO for short).
Based on the previous 1, we continue to relax the order of write-write operations in the program, resulting in the partial store order memory model (referred to as PSO).
Based on the previous 1 and 2, we continue to relax the order of read-write and read-read operations in the program, resulting in the relaxed memory order memory model (referred to as RMO) and the PowerPC memory model.
Note that the processor's relaxation of read/write operations here is based on the premise that there is no data dependency between the two operations (because the processor must comply with as-if-serial semantics, the processor does not Two memory operations with data dependencies will be reordered).
The following table shows the detailed characteristics of common processor memory models:
Memory model name
Corresponding processor
Store-Load reordering
Store-Store reordering
Load-Load and Load-Store reordering
Writes from other processors can be read earlier
Writes from the current processor can be read earlier
TSO
sparc-TSOX64
Y
Y
PSO
sparc-PSO
Y
Y
Y
RMO
ia64
Y
Y
Y
Y
PowerPC
PowerPC
Y
Y
Y
Y
Y
In this table we can see all the processor memory The models all allow write-read reordering for the reason explained in Chapter 1: they all use write buffers, which may cause write-read operations to be reordered. At the same time, we can see that these processor memory models allow earlier reads of writes to the current processor. The reason is also because of the write cache area: Since the write cache area is only visible to the current processor, this feature causes the current processor to be able to Other processors see writes that are temporarily held in their own write buffers first.
The various processor memory models in the table above, from top to bottom, the models change from strong to weak. The more performance-focused a processor is, the weaker the memory model will be. Because these processors want the memory model to constrain them as little as possible so that they can do as many optimizations as possible to improve performance.
Since the common processor memory model is weaker than the JMM, when the Java compiler generates bytecode, it will insert memory barriers at appropriate locations in the execution instruction sequence to limit processor reordering. At the same time, since the memory models of various processors have different strengths and weaknesses, in order to present a consistent memory model to programmers on different processor platforms, the number and types of memory barriers that JMM needs to insert in different processors also vary. Are not the same. The following figure shows a schematic diagram of the memory barriers that JMM needs to insert in different processor memory models:
As shown in the figure above, JMM masks the differences in different processor memory models. , which presents Java programmers with a consistent memory model across different processor platforms.
JMM, the relationship between processor memory model and sequential consistency memory model
JMM is a language-level memory model, and the processor memory model is a hardware-level memory model with consistent sequence The sexual memory model is a theoretical reference model. The following is a diagram comparing the strengths and weaknesses of the language memory model, processor memory model and sequential consistency memory model:
From the above figure we can see that the four common processor memory models are weaker than the three commonly used language memory models. Both the processor memory model and the language memory model are weaker than the sequential consistency memory model. Like the processor memory model, the more a language pursues execution performance, the weaker the memory model design will be.
Design of JMM
From the perspective of a JMM designer, two key factors need to be considered when designing JMM:
The programmer's use of the memory model. Programmers want memory models that are easy to understand and easy to program. Programmers want to write code based on a strong memory model.
Compiler and processor implementation of the memory model. Compilers and processors want the memory model to constrain them as little as possible so that they can do as many optimizations as possible to improve performance. Compilers and processors want to implement a weak memory model.
Since these two factors are contradictory, the core goal of the JSR-133 expert group when designing JMM is to find a good balance point: on the one hand, it must provide programmers with a strong enough memory visibility guarantee; On the other hand, compiler and processor restrictions should be relaxed as much as possible. Let's take a look at how JSR-133 achieves this goal.
For specific explanation, please see the sample code for calculating the area of a circle mentioned earlier:
double pi = 3.14; //A double r = 1.0; //B double area = pi * r * r; //C
The above sample code for calculating the area of a circle has three happens-before relationships:
A happens- before B;
B happens- before C;
A happens- before C;
Since A happens- before B, the definition of happens- before will require: the result of the execution of operation A It should be visible to B, and the execution order of A operation should be before B operation. However, from the perspective of program semantics, reordering A and B will not change the execution results of the program, but can also improve the execution performance of the program (allowing this reordering reduces the constraints on compiler and processor optimization ). In other words, among the above three happens-before relationships, although 2 and 3 are necessary, 1 is unnecessary. Therefore, JMM divides the reordering prohibited by happens-before requirements into the following two categories:
Reordering that will change the program execution results.
Will not change the reordering of program execution results.
JMM adopts different strategies for these two different natures of reordering:
For reordering that will change the execution results of the program, JMM requires the compiler and processor to prohibit such reordering.
JMM does not place requirements on the compiler and processor for reordering that does not change the execution results of the program (JMM allows such reordering).
The following is the design diagram of JMM:
Two points can be seen from the above picture:
The happens-before rules provided by JMM to programmers Can meet the needs of programmers. JMM's happens-before rules are not only simple and easy to understand, but also provide programmers with strong enough memory visibility guarantees (some memory visibility guarantees do not necessarily actually exist, such as A happens-before B above).
JMM has as few constraints on the compiler and processor as possible. From the above analysis, we can see that JMM actually follows a basic principle: as long as the execution results of the program are not changed (referring to single-threaded programs and correctly synchronized multi-threaded programs), the compiler and processor can be optimized no matter how they are used. . For example, if the compiler determines after careful analysis that a lock will only be accessed by a single thread, the lock can be eliminated. For another example, if the compiler determines after careful analysis that a volatile variable will only be accessed by a single thread, then the compiler can treat the volatile variable as an ordinary variable. These optimizations will not change the execution results of the program, but also improve the execution efficiency of the program.
JMM's memory visibility guarantee
The memory visibility guarantee of Java programs can be divided into the following three categories according to program type:
Single-threaded programs. Single-threaded programs do not have memory visibility issues. The compiler, runtime, and processor work together to ensure that the execution results of a single-threaded program are the same as the program's execution results in the sequential consistency model.
Correctly synchronized multi-threaded programs. The execution of a properly synchronized multithreaded program will have sequential consistency (the results of the program's execution will be the same as if the program were executed in a sequentially consistent memory model). This is the focus of JMM, which provides programmers with memory visibility guarantees by limiting compiler and processor reordering.
Unsynchronized/incorrectly synchronized multi-threaded programs. JMM provides them with minimal safety guarantees: the value read when a thread executes is either the value written by a previous thread or the default value (0, null, false).
The following figure shows the similarities and differences in the execution results of these three types of programs in JMM and in the sequential consistent memory model:
As long as the multi-threaded program is correctly synchronized, JMM guarantees that the execution results of the program on any processor platform are consistent with the execution results of the program in the sequentially consistent memory model.
JSR-133's fixes to the old memory model
JSR-133's fixes to the old memory model before JDK5 mainly include two:
Enhance volatile memory semantics. The old memory model allowed volatile variables to be reordered with ordinary variables. JSR-133 strictly limits the reordering of volatile variables and ordinary variables, so that volatile write-read and lock release-acquire have the same memory semantics.
Enhance the memory semantics of final. In the old memory model, the value of the same final variable may be different when read multiple times. To this end, JSR-133 adds two reordering rules for final. Final now has initialization safety.
The above is the in-depth analysis of Java memory model: summary. For more related content, please pay attention to the PHP Chinese website (m.sbmmt.com)!