In-depth understanding of the Java memory model (7)-summary

Processor memory model

The sequential consistency memory model is a theoretical reference model. JMM and processor memory models usually take the sequential consistency memory model as a reference when designing. The JMM and processor memory model will relax the sequential consistency model during the design, because if the processor and JMM are implemented completely according to the sequential consistency model, then many processor and compiler optimizations will be prohibited. Execution performance will have a great 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:

  1. Relax the order of write-read operations in the program, resulting in the total store ordering memory model (referred to as TSO).
  2. On the basis of the previous 1, continue to relax the order of write-write operations in the program, resulting in the partial store order memory model (referred to as PSO).
  3. On the basis of the previous 1 and 2, 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 the as-if-serial semantics, the processor will not be dependent on the data The two memory operations are 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

Can read writes from other processors earlier

Can read the write of the current processor earlier

TSO

sparc-TSO

X64

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 that all processor memory models allow write-read reordering. The reason is explained in the first chapter: they all use the write buffer area, and the write buffer area may lead to write-read operation reordering. . At the same time, we can see that these processor memory models all allow earlier reading of the current processor’s writes. The reason is also because of the write buffer area: since the write buffer area is only visible to the current processor, this feature makes the current processor comparable The other processors first see the writes temporarily stored in their write buffers.

For the various processor memory models in the table above, from top to bottom, the model changes from strong to weak. The more the processor pursues performance, the weaker the memory model design will be. Because these processors hope that the less constrained by their memory model, the better, so they can do as many optimizations as possible to improve performance.

Since the common processor memory model is weaker than JMM, when the java compiler generates bytecode, it inserts a memory barrier at the appropriate position of the execution instruction sequence to limit the reordering of the processor. At the same time, because the memory models of various processors are not the same in strength, in order to show programmers a consistent memory model on different processor platforms, the number and types of memory barriers that JMM needs to insert in different processors are also different. 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 above figure, JMM shields the differences in memory models of different processors, and it presents a consistent memory model for java programmers on different processor platforms.

 

JMM, the relationship between the processor memory model and the sequential consistency memory model

JMM is a language-level memory model, the processor memory model is a hardware-level memory model, and the sequential consistency memory model is a theoretical reference model. The following is a schematic diagram of the strengths and weaknesses of the language memory model, the processor memory model, and the sequential consistency memory model:

From the above figure, we can see that the four common processor memory models are weaker than the commonly used three language memory models, and 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 the language pursues execution performance, the weaker the memory model design will be.

 

JMM design

From the perspective of a JMM designer, there are two key factors to consider when designing a JMM:

  • The programmer's use of the memory model. Programmers want the memory model to be easy to understand and easy to program. Programmers want to write code based on a strong memory model.
  • The implementation of the memory model by the compiler and processor. Compilers and processors hope that the less constrained by the memory model, the better, so that they can do as many optimizations as possible to improve performance. Compilers and processors hope to implement a weak memory model.

Because these two factors contradict each other, the core goal of the JSR-133 expert group when designing JMM is to find a good balance: on the one hand, it must provide programmers with a strong enough memory visibility guarantee; on the other hand, the compiler The limits of the device and the processor should be relaxed as much as possible. Let us 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:

  1. A happens- before B;
  2. B happens- before C;
  3. A happens- before C;

Since A happens- before B, the definition of happens- before requires that the result of operation A must be visible to operation B, and the execution order of operation A is arranged before operation B. But from the perspective of program semantics, reordering A and B will not change the execution result of the program, but also improve the execution performance of the program (allowing this reordering reduces the constraints on the compiler and processor optimization ). In other words, in the above three happens-before relations, although 2 and 3 are necessary, 1 is unnecessary. Therefore, JMM divides the reordering prohibited by the happens-before requirement into the following two categories:

  • Will change the reordering of program execution results.
  • Will not change the reordering of program execution results.

JMM adopts different strategies for these two different types of reordering:

  • For reordering that will change the results of program execution, JMM requires the compiler and processor to prohibit such reordering.
  • For reordering that does not change the execution result of the program, JMM does not require the compiler and processor (JMM allows this reordering).

The following is a schematic diagram of the JMM design:

Two points can be seen from the above figure:

  • 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 memory visibility guarantees (some memory visibility guarantees may not 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 is actually following a basic principle: as long as the execution result of the program is not changed (referring to a single-threaded program and a correctly synchronized multi-threaded program), the compiler and the processor can be optimized regardless of . For example, if the compiler determines that a lock can only be accessed by a single thread after careful analysis, the lock can be eliminated. For another example, if the compiler determines that a volatile variable can only be accessed by a single thread after careful analysis, the compiler can treat the volatile variable as an ordinary variable. These optimizations will not change the execution result of the program, but can also improve the execution efficiency of the program.

JMM's memory visibility guarantee

The memory visibility guarantees of Java programs can be divided into the following three categories according to program types:

  1. Single-threaded program. Single-threaded programs will not have memory visibility issues. The compiler, runtime, and processor work together to ensure that the execution result of a single-threaded program is the same as the execution result of the program in the sequential consistency model.
  2. Properly synchronized multithreaded programs. The execution of a correctly synchronized multithreaded program will have sequential consistency (the execution result of the program is the same as the execution result of the program in the sequential consistency memory model). This is the focus of JMM. JMM provides programmers with memory visibility guarantees by restricting the reordering of compilers and processors.
  3. Multithreaded programs that are not synchronized/not properly synchronized. JMM provides them with the minimum security guarantee: the value read during the execution of the thread is either the value written by a previous thread or the default value (0, null, false).

The following figure shows the similarities and differences between the execution results of these three types of programs in JMM and in the sequential consistent memory model:

As long as the multithreaded program is correctly synchronized, JMM guarantees that the execution result of the program on any processor platform is consistent with the execution result of the program in the sequential consistency memory model.

JSR-133 fixes to the old memory model

JSR-133 fixes the old memory model before JDK5 mainly in two ways:

  • Enhance the memory semantics of volatile. The old memory model allows volatile variables to be reordered with ordinary variables. JSR-133 strictly restricts the reordering of volatile variables and ordinary variables, so that volatile write-read and lock release-acquisition 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 if it is read multiple times. For this reason, JSR-133 adds two reordering rules for final. Now, final has initialization security.
Previous article   Deep understanding of Java memory model (6)-final keyword

Thanks to the author for his contribution to this article

Cheng Xiaoming, Java software engineer, nationally certified system analyst and information project manager. Focus on concurrent programming and work at Fujitsu Nanda. Personal email: [email protected].
---------------------
Author: World coding
Source: CSDN
Original: https://blog.csdn.net/dgxin_605/article/details/86184191
Copyright: This article is the original article of the blogger, please attach a link to the blog post if you reprint it! !

Guess you like

Origin blog.csdn.net/dgxin_605/article/details/86184191