Java memory model

 9 minutes to read

java memory model

Java memory model (Java Memory Model (JMM) is a specification that meets the memory model, blocking the differences between the visits of various hardware and operating systems, and ensuring that the Java program access to memory under various platforms can guarantee that the memory access can be guaranteed Consistent mechanisms and norms.

In short, JMM is a specification of JVM, defining the memory model of JVM. It blocks the differences between the visits of various hardware and operating systems. Unlike C, it is relatively safe to access hardware memory like C.
Its main purpose is to solve the problems that the existence of local memory data is inconsistent when the multi -threaded communication is communicated through shared memory, the compiler will be sorted on the code instruction, and the processor will be executed by the processor.
It can ensure the atomicity, visibility and order of concurrent programming scenes.

Atomicity: Each operation is atomic, an indispensable

Visible: It means that when a thread modifies the value of a shared variable, whether the other threads can immediately know the change, JMM stipulates that all variables are stored in the main memory.

Order: Support instruction rearrangement.

Here explain in detail about order

What is orderly?

For the execution code of a thread, we are always used to thinking that the execution of the code always enforces in an orderly manner. But in order to improve performance, compilers and processors usually sort the instruction sequence. The Java specification stipulates that the JVM thread maintains the semantic semaphysics internally, that is, as long as the final result of the program is equal to the results of its orderly execution, the execution order of the instruction can be inconsistent with the code order. This process is called the heavy sorting of the instruction.

Ordering advantages and disadvantages

The JVM can be appropriately sorted by the processor characteristics (the CPU multi -stage cache system, multi -core processor, etc.), so that the machine instructions can better meet the execution characteristics of the CPU and maximize the performance of the machine. However, instructions can ensure the consistency of serial semantics, but there is no obligation to ensure that the semantics between multi -threading are consistent (that is, “dirty reading”). Simply put, the irrelevant code of two or more may be executed first when executing. It is not the first one. It may not be implemented from top to bottom. The execution order will be optimized.

In the single -threaded environment, ensure that the final execution results of the program are consistent with the results of the code order.

The processor must consider the data dependencies between instructions when sorting heavy sorting.

The multi -threaded environment is executed alternately. Due to the existence of the compiler optimization, whether the variables used in the two threads can ensure that consistency cannot be determined, and the results cannot be predicted.

Java memory model specifies that all variables are stored in the main memory, and each thread also has its own work memory. The work memory of the thread saves the copy of the main memory copy of the variable used by the thread. All operations (reading, assignment, etc.) of the thread must be performed in the work memory. Can not directly read and write variables in the main memory. The variables in the opponent’s work memory cannot be accessed between different threads.

Main memory: It mainly corresponds to the object instance data part of the Java pile. (Register, cache at high speed)

Work memory: Some areas corresponding to the virtual machine stack. (Hardware memory)

Regarding the specific interactive agreement between the main memory and the work memory, that is, how a variable is copied from the main memory to the work memory and how to synchronize from the work memory to the main memory. When the Java virtual machine is implemented, every operation mentioned in the following is atoms and cannot be divided:

LOCK (lock): Variables acting on the main memory, it identifies a variable as a state of a thread exclusively.

Unlock: Variables acting on the main memory. It releases a variable in a lock state, and the variable after release can be determined by other threads.

Read (read): Variables acting on the main memory. It transmits the value of a variable from the main memory to the work memory of the thread so that the subsequent LOAD action is used.

LOAD (load): Variables acting for work memory, it puts the variable values ​​obtained by the read operation from the main memory of the main memory into a copy of the work memory.

Use (use): Variables acting for work memory, it transmits the value of a variable in the work memory to the execution engine. Whenever a virtual opportunity is used to perform a bytecode instruction that needs to use a variable value, this operation will be performed.

ASSIGN: Variables acting for work memory, it performs this operation when a value symbol received from the execution engine, whenever a virtual opportunity is associated with a byte code instruction assigned by the variable.

Store (storage): Variables acting for work memory, it transmits the value of a variable in the work memory to the main memory for subsequent WRITE operations.

Write: The variables acting on the main memory, it puts the value of the variable obtained from the work memory into the variable of the main memory.

  • If you perform a lock operation on a variable, the value of this variable in the work memory will be cleared;
  • Before performing the Unlock operation on a variable, this variable must be synchronized to the main memory.
memory barrier (Memory Barrier)

The memory barrier of the hardware layer is divided into two types: Load Barrier and Store Barrier read barriers and write barriers (the memory barrier is the hardware layer).

Why do you need a memory barrier

Since the modern operating system is a multi -processor operating system, each processor will have its own cache, and there may be problems with different processors cache. And because the operating system may have heavy sorting, which leads to the error data, the operating system provides some memory barriers to solve this problem.

simply put:

  1. Different threads performed in different CPUs to the cache value of the same variable different

  2. Volatile can solve the above problems. Different hardware’s implementation of memory barriers is different. Java shields these differences and generates the memory barrier through JVM. For reading barrier: Inserting the read barrier before the instruction, the data in the high -speed cache can fail and forcibly access from the master.

The role of memory barrier

The CPU execution instruction may be disorderly, it has two more important functions

  1. Stop the instructions on both sides of the barrier to sort
  2. Forced to write the dirty data in the writing buffer/high -speed cache, etc., and write back to the main memory to make the corresponding data in the cache fail.
volatile type variable

When we declare that a variable is modified by Volatile, this variable has a thread visibility. Volatile adds a memory barrier by adding a memory barrier before and after reading and writing.

Volatile type variable has the following features

  1. Visuality. For the reading of this variable, you can definitely see the last writing before reading.
  2. Prevent instructions from sorting. When executing code, in order to improve the execution efficiency, the instruction will be re -sorted without affecting the final result. Use volatile to prevent it. For example

Note that volatile does not have atomicity

As for how the bottom layer of the VOLATile realizes the visibility of different threads, the hardware involves the hardware. When the variable modified by Volatile is written, a special assembly instruction will be generated. This instruction will trigger the MESI protocol. There will be a bus sniffing mechanism. In short, this CPU will constantly detect the change of the variable in the bus. If the variable changes, due to this sniffing mechanism, other CPUs will immediately clear the CPU cache data of the variable, Re -deposit this data from the main memory.

HAPEPENS-BEFORE

In the JMM principle in Java language, there is a principle of “happens-bEFORE”. This principle is very important:

It is a very useful means to determine whether the data is competitive and whether the thread is safe. Relying on this principle, we can solve all problems between the two operations in the concurrent environment through several simple rules and rules, without need to fall into the underlying compilation principles of the bitter and difficult to understand the Java memory model.

In JMM, if the result of one operation execution needs to be sorted by another operation visibility or code, then the principle

In short, the HAPENS-BEFORE principles are:

If one operation occurs in another operation, the execution result of the first operation will be visible to the second operation, and the order of the execution result of the first operation is in front of the second operation.

There is a HAPENS-BEFORE relationship between the two operations, which does not mean that it must be implemented in accordance with the order of the HAPENS-BEFORE principles. If the execution results after the sorting are consistent with the results of the execution according to the Happets-BEFORE relationship, then this sort is not illegal.

Happens-Before has the following eight principles:

Sequence rules: In a thread, in the order of the code, the operation written in the front occur in the previous operation.

Locking rules: an unlock operation first occurs in the back (“behind” refers to the time in time) to the same lock lock operation

Volatile variable rules: The writing operation of a Volatile variable first occurs in the reading operation of this variable.

Passing rules: If operation A first occurs in operation B, and operation B occurs first in operation C, you can get the operation A first in operation C

Thread Start Rule: Start () method of Thread objects first occurs in each action of this thread

Thread interruption rules (Thread Interruption Rule): The call of the thread interrupt () method is first, which occurs in the interrupted thread. () Detecting whether an interrupt occurs.

Thread Termination Rule: All operations in the thread occur in the termination detection of this thread. We can check whether the thread has been terminated through iSalive () and other means.

Finalizer Rule: The initialization of an object (the end of the structure of the constructor) first occurs at the beginning of its finalize () method.