Java memory partitioning

The JMM defines two types of memory: primary memory and working memory. Here the main memory and working memory to the JVM memory division (heap, stack, method) was conducted in different level, if must correspond, main memory is corresponding to the object instance is part of the Java heap, working memory is the corresponding parts of the stack area, from the lower level, main memory is the corresponding hardware physical memory, Working memory corresponds to registers and caches. As shown in the figure:

Normally, all data in Java is stored in main memory, as shown in the following figure:

With the development of CPU technology, the execution of CPU speed faster and faster, but the memory of the technology is not much change, if JAVA thread every read and write variables directly operating main memory, have more influence on the performance, so each thread has respective working memory, working memory is the main memory of a copy of the variables, The thread reads and writes variables directly in the working memory, not directly in the main memory variables.

Therefore, in order to “squeeze” processing performance and achieve “high concurrency”, a cache is added to the CPU as a buffer.

The problem with this, however, is that when a thread changes a variable in its own working memory, it is not visible to other threads, leading to thread-unsafe problems. This is the cache consistency issue.

Cache consistency

Phones on the market today usually have two or more cpus, some of which have multiple cores. Each CPU can run one thread at a time, which means that if your Java program is multithreaded, it is possible for multiple threads to be executed by different cpus at the same time.

Instruction rearrangement

In addition to the cache consistency problem, there is another hardware problem that is also important: to maximize the utilization of the CPU’s internal arithmetic units, the processor may reorder the input bytecode instructions, known as processor optimization. In addition to cpus, compilers for many programming languages have similar optimizations, such as the Just-in-time compiler (JIT) for the Java Virtual machine that does instruction reordering.

For example:

int a = 1;
int b = 1;
a = a + 1;
123
Copy the code

After CPU instructions are rearranged, this code might look like this:

int a = 1;
a = a + 1;
int b = 1;
123
Copy the code

If we allow CPU optimizations or compiler instructions to be rearranged, we can write Java code that performs surprisingly well. To solve this problem, the Java Virtual Machine specification proposes a mechanism — the Java Memory Model — to make Java code output consistent across different hardware and operating systems.

What is the Java Memory model

JMM (Java Memory Model) is the Java Memory Model. JMM defines the access rules for each shared variable in the program, that is, the low-level details such as storing variables into and reading variables from Memory in the virtual machine. It solves the memory access problems caused by CPU multi-level cache, CPU optimization and instruction rearrangement, so as to ensure that Java programs (especially multithreaded programs) have the same memory access effect on various platforms.

Memory interoperation

There are eight types of memory interactions, and the virtual machine implementation must ensure that each operation is atomic and non-separable. (For double and long variables, exceptions are allowed for load, store, read, and write on some platforms.)

  • Lock: A variable that acts on main memory, marking a variable as thread-exclusive
  • Unlock: A variable that acts on main memory. It releases a locked variable so that it can be locked by another thread
  • Read: Acts on a main memory variable that transfers the value of a variable from main memory to the thread’s working memory for subsequent load action
  • Load: Variable acting on working memory, which puts a read operation variable from main memory into working memory
  • Use: Applies to variables in working memory. It transfers variables in working memory to the execution engine. This instruction is used whenever the virtual machine reaches a value that requires the variable to be used
  • Assign: A variable applied to working memory that places a value received from the execution engine into a copy of the variable in working memory
  • Store: Acts on a variable in main memory. It transfers a value from a variable in working memory to main memory for subsequent writes
  • Write: A variable in main memory that puts the value of a variable in main memory from the store operation in working memory

The JMM lays down the following rules for the use of these eight directives:

  • One of the read and load, store and write operations is not allowed to occur separately. If you use read, you must load; if you use store, you must write
  • A thread is not allowed to discard its most recent assign operation, in which it must inform main memory that the work variable’s data has changed
  • A thread is not allowed to synchronize unassigned data from working memory back to main memory
  • A new variable must be created in main memory. Working memory is not allowed to use an uninitialized variable directly. Assign and load operations must be performed before use and store operations are performed on variables
  • Only one thread can lock a variable at a time. You must unlock the device for the same number of times
  • If a variable is locked, all of its values in working memory will be emptied. Before the execution engine can use the variable, the variable must be reloaded or assigned to initialize its value
  • You cannot unlock a variable if it is not locked. You cannot unlock a variable that is locked by another thread
  • Before an UNLOCK operation can be performed on a variable, it must be synchronized back to main memory

Happens-before principle

The antecedent principle is used to describe memory visibility between two operations. This means that when operation A takes place before operation B, the influence of operation A can be observed by OPERATION B when operation B takes place. “Influence” includes changing the value of shared variables in memory, sending messages, calling methods, etc.

The JMM defines the following situations that automatically comply with the happens-before rule:

  • Program Order rules: In a thread, the execution rules of a Program are the same as the writing rules of the Program, and are executed from top to bottom.
  • Monitor Lock Rule: In both single-threaded and multi-threaded environments, a Lock must be unlocked before it can be locked.
  • Volatile Variable rules: For a volatile Variable, the first write must precede the subsequent read
  • Thread Start Rule: The Start () method of a Thread object precedes every action of the Thread. Given that thread A starts ThreadB by executing threadb.start () during execution, thread A’s changes to the shared variables are guaranteed to be visible to ThreadB after ThreadB starts executing.
  • Thread Termination Rule: The Termination detection operations (such as thread.join () and thread.isalive ()) on the Thread object must be performed later than all operations in the Thread
  • Interruption Rule: A call to the Thread Interruption () that detects the occurrence of thread.interrupted () before the invoked Thread does
  • Object termination Finalizer Rule: An object’s initialization method executes the Finalizer() method before one
  • Transitivity: If operation A precedes operation B and operation B precedes operation C, operation A precedes operation C

conclusion

  • The source of the Java memory model: mainly because optimizations such as CPU caching and instruction reordering can cause uncontrollable results in multithreaded programs.

  • What the Java memory model is: It is essentially a set of specifications with one of the most important happens-before principles in it.

reference

Pull education – Android engineer advanced 34 speak

This article was first published on my blog: the Java JMM Memory Model

More articles please pay attention to my public number: code nong workplace