#1. Overview
Multi-tasking and high concurrency are one of the important indicators to measure the capability of a computer processor one. Generally, to measure the performance of a server, the indicator Transactions Per Second (TPS) is more illustrative. It represents the average number of requests that the server can respond to in one second, and the TPS value is related to the program's Concurrency capabilities are very closely related. Before discussing the Java memory model and threads, let's briefly introduce the efficiency and consistency of hardware. (Recommended:java video tutorial)
2. Hardware efficiency and consistency
Due to the computer’s storage device There is a gap of several orders of magnitude between the memory and the processor's computing power, so modern computer systems have to add a layer of cache (cache) with a read and write speed as close as possible to the processor's computing speed as a buffer between the memory and the processor. Buffering: Copy the data needed for the operation to the cache so that the operation can be performed quickly. When the operation is completed, it is synchronized back to the memory from the cache so that the processor does not need to wait for slow memory reading and writing.
Cache-based storage interaction solves the speed conflict between the processor and the memory, but introduces a new problem: Cache Coherence. In a multiprocessor system, each processor has its own cache, and they share the same main memory.
As shown in the figure below: Multiple processor computing tasks involve the same main memory, and a protocol is needed to ensure data consistency. Such protocols include MSI, MESI, MOSI, and Dragon Protocol. The memory access operations defined in the Java virtual machine memory model are comparable to the hardware cache access operations. The Java memory model will be introduced later.
In addition, in order to make full use of the computing unit inside the processor, the processor may perform out-of-order execution of the input code (Out-Of -Order Execution) optimization, the processor will reorganize the results of the code executed out of order after calculation to ensure the accuracy of the results. Similar to the processor's out-of-order execution optimization, the Java virtual machine's just-in-time compiler also has similar instruction reordering (Instruction Recorder) optimization.
3. Java memory model
Defining the Java memory model is not an easy task. This model must be defined rigorously enough to Let Java's concurrent operations not cause ambiguity; however, it must also be loose enough so that the implementation of the virtual machine has enough free space to utilize various features of the hardware (registers, caches, etc.) to obtain better execution speed . After a long period of verification and patching, the Java memory model has matured and improved since the release of JDK1.5.
3.1 Main memory and working memory
The main goal of the Java memory model is to define the access rules for each variable in the program, that is, to store the variables into memory in the virtual machine and low-level details like retrieving variables from memory. The variables here are different from the variables mentioned in Java programming. They include instance fields, static fields and elements that constitute array objects, but do not include local variables and method parameters. The latter are private to the thread and will not be shared. .
The Java memory model stipulates that all variables are stored in the main memory. Each thread also has its own working memory (can be compared with the cache of the processor previously). The working memory of the thread A copy of the variables used by the thread is saved to the main memory. All operations (reading, assignment) of the variables by the thread must be performed in the working memory, and variables in the main memory cannot be directly read or written.
Different threads cannot directly access the variables in each other's working memory. The transfer of variable values between threads needs to be completed in the main memory. The interactive relationship between threads, main memory and working memory is shown in the figure below, and The picture above is very similar.
The main memory and working memory here are not the same level of memory division as the Java heap, stack, and method area of the Java memory area.
3.2 Inter-memory interaction
About the specific interaction protocol between main memory and working memory, that is, how a variable is copied from main memory to working memory, and how to copy it from main memory to working memory. To implement the details of synchronizing working memory to main memory, the Java memory model defines the following eight operations to complete:
1. Lock: Acts on variables in main memory, identifying a variable as a Thread exclusive state.
2. Unlock: Acts on the main memory variable to release a variable that is in a locked state. Only the released variable can be locked by other threads.
3. read: Acts on the main memory variable, transferring a variable value from the main memory to the working memory of the thread, so that it can be used in the subsequent load action
4. Load: Acts on variables in working memory. It puts the variable value obtained from the main memory by the read operation into a copy of the variable in the working memory.
5. Use (use): Acts on variables in the working memory, passing a variable value in the working memory to the execution engine. Whenever the virtual machine encounters a bytecode instruction that needs to use the value of the variable will perform this operation.
6. Assign (assignment): Acts on variables in working memory. It assigns a value received from the execution engine to a variable in working memory. Whenever the virtual machine encounters a bytecode that assigns a value to a variable, Perform this operation when commanded.
7. Store (storage): Acts on variables in the working memory, transferring the value of a variable in the working memory to the main memory for subsequent write operations.
8. Write: Acts on variables in main memory. It transfers the store operation from the value of a variable in the working memory to the variable in the main memory.
If you want to copy a variable from the main memory to the working memory, you need to perform the read and load operations in sequence. If you want to synchronize the variable from the working memory back to the main memory, you need to perform the read and load operations in sequence. Perform store and write operations.
The Java memory model only requires that the above operations must be executed in order, but there is no guarantee that they must be executed continuously. That is, other instructions can be inserted between read and load, and between store and write. For example, when accessing variables a and b in main memory, the possible order is read a, read b, load b, load a. The Java memory model also stipulates that when performing the above eight basic operations, the following rules must be met:
1. One of the read and load, store and write operations is not allowed to appear alone
2. A thread is not allowed to discard its most recent assign operation, that is, variables must be synchronized to main memory after they are changed in working memory.
3. A thread is not allowed to synchronize data from the working memory back to the main memory for no reason (no assign operation has occurred).
4. A new variable can only be created in the main memory, and an uninitialized (load or assign) variable is not allowed to be used directly in the working memory. That is, before performing use and store operations on a variable, assign and load operations must be performed first.
5. Only one thread is allowed to lock a variable at the same time. Lock and unlock must appear in pairs.
6. If a lock operation is performed on a variable, the work will be cleared. The value of this variable in the memory needs to be re-executed load or assign operation to initialize the variable value before the execution engine uses this variable
7. If a variable has not been locked by the lock operation in advance, it is not allowed to be unlocked operation; it is also not allowed to unlock a variable locked by other threads.
8. Before performing an unlock operation on a variable, the variable must be synchronized to the main memory (perform store and write operations).
3.3 Reordering
In order to improve performance when executing a program, compilers and processors often reorder instructions. Reordering is divided into three types:
1. Compiler optimized reordering. The compiler can rearrange the execution order of statements without changing the semantics of a single-threaded program.
2. Instruction-level parallel reordering. Modern processors use instruction-level parallelism to overlap execution of multiple instructions. If there are no data dependencies, the processor can change the order in which statements correspond to machine instructions.
3. Reordering of the memory system. Because the processor uses cache and read and write buffers, this can make load and store operations appear to be executed out of order.
From the Java source code to the final actual executed instruction sequence, the following three reorderings will be made:
In order to ensure the visibility of the memory, the Java compiler generates the instruction sequence Memory barrier instructions are inserted where appropriate to disable certain types of processor reordering. The Java memory model divides memory barriers into four types: LoadLoad, LoadStore, StoreLoad and StoreStore:
For more java knowledge, please pay attention to thejava Basic Tutorialcolumn.
The above is the detailed content of Detailed explanation of Java memory model with pictures and text. For more information, please follow other related articles on the PHP Chinese website!