Biography: Swing unruly, love life. Java Cultivator (wechat official ID: Java Cultivator), welcome to follow. A detailed mind map of the 12 uses of @Pointcut and a summary of the 2020 interview questions is available

Volatile and synchronized are always encountered in interviews, concurrent programming, and some open source frameworks. How does synchronized ensure concurrency security? What does memory visibility of volatile semantics mean? What does this have to do with the JMM, what is the role of the JMM in concurrent programming, and why is it needed? How is it different from the JVM memory structure?

Summarizes the core knowledge points and interview focus, illustrated fearless interview and concurrent programming, comprehensive promotion of concurrent programming internal work!

  1. What are the differences between JMM and JVM memory structures?

  2. What is the JMM (Java Memory Model) Memory Model and how does it relate to concurrent programming?

  3. The most important aspects of the memory model: instruction rearrangement, atomicity, memory visibility.

  4. What does volatile memory visibility mean? Its application scenarios and common mistakes in the use of pit avoidance guide.

  5. The relationship between the realization principle of synchronized and monitor was analyzed.

JVM memory versus the JMM memory model

Next, we learn about THE JVM memory structure and THE JMM memory model by means of graphics, DJ, trop the Beat, lets’ Go!

The JVM memory structure is such that it needs to be nagged along with the running data of the VIRTUAL machine, because the data area of the program needs to be sorted out.

The Java memory model is also fascinating, and not to be confused with the JVM memory structure, which is in fact an abstract definition designed primarily to secure access to data for concurrent programming.

To sum up:

  • The JVM memory structure is related to the Runtime region of the Java VIRTUAL machine;

  • The Java memory model is related to concurrent programming in Java.

JVM memory structure

Java code runs on the virtual machine. The.java files we write are first compiled into.class files, which are then loaded by the JVM and translated into machine code for each operating system platform

The JVM loads the javAC-compiled class files through the Java classloader, and calls the system interface to execute the program through the execution engine interpretation or JIT just-in-time compilation calls.

The virtual machine will divide the memory into different data areas when running programs, and different areas are responsible for different functions. With the development of Java, the memory layout is also under adjustment-XX:PermSize -XX:MaxPermSizeIsoparameters become meaningless.JVM memory structureAs shown below:

The module that executes the bytecode is called the execution engine, which relies on program counters to restore thread switching. Local memory contains the metadata area as well as some direct memory.

Heap (Heap)

The data sharing area stores instance objects and arrays, and is usually the most memory-consuming area that is also shared. For example, new Object() generates an instance; Arrays are also stored on the heap, because arrays are objects in Java. The main scope of the garbage collector.

When an object is created, is it allocated on the heap or on the stack? This has to do with two things: the type of the object and its location in the Java class.

Java objects can be divided into basic data types and ordinary objects.

For normal objects, the JVM creates the object on the heap first, and then uses references to it elsewhere. For example, store this reference in a local variable table in the virtual machine stack.

For basic data types (byte, short, int, long, float, double, char), there are two cases.

As we mentioned above, each thread has a virtual machine stack. When you declare an object of primitive data type in the method body, it is allocated directly on the stack. Other cases, usually allocated on the heap, may be allocated on the stack in the case of escape analysis.

Note that things like the int[] array are allocated on the heap. Arrays are not basic data types.

Java Virtual Machine Stacks

Java virtual machine based on thread stack, even if only one main method, are based on threads run, in the life cycle of operation, to participate in the calculation of data will be out of the stack and the stack, and stack “virtual machine” inside each of the data is the “stack frame”, is created when Java method performs a “stack frame” into the stack stack “virtual machine”. When the call ends, the “stack frame” goes off the stack and the corresponding thread terminates.

public int add() { int a = 1, b = 2; return a + b; }Copy the code

The add method is abstracted into a “stack frame” structure. During the execution of the method, the corresponding operands 1 and 2 are pushed onto the stack and assigned to local variables A and B. When the add instruction is encountered, the results of the sum of operands 1 and 2 are pushed onto the stack. After the method ends, the “stack frame” is removed from the stack and the result is returned.

Each stack frame contains four regions:

  1. Table of local variations: basic data types, object references, retuenAddress Pointers to bytecode;

  2. The operand stack

  3. Dynamic connection

  4. The return address

Here’s an important place to knock on the blackboard:

  • There are actually two layers of meaning of stack, the first layer is the “stack frame” corresponding method; The second layer corresponds to the execution of the method, corresponding to the operand stack.

  • All bytecode instructions are abstracted as stack loading and stack unloading operations. The execution engine just foolishly executes in order to make sure it’s correct.

Each thread has a “virtual stack,” and each “virtual stack” has multiple “stack frames,” which correspond to a method. Each “stack frame” contains a local variable table, operand stack, dynamic link, and method return address. The completion of the method means that the “stack frame” is off the stack.

The Method Area meta-space

Stores metadata information for each class class, such as class structure, constant pool at run time, fields, method data, method constructors, and special methods such as interface initialization.

Student: Is the meta-space on the heap?

A: It is not allocated on the heap, but in the off-heap space. The method area is in the meta-space.

In which region is the string constant pool?

A: This is different from JDK versions. Prior to JDK 1.8, the metaclass was not formed, and the method area was placed in a space called the permanent generation, where string constants were located.

Prior to JDK 1.7, string constant pools were also placed in a space called the permanent strip. After JDK 1.7, the string constant pool was moved from the permanent generation to the heap.

So, since version 1.7, the string constant pool has always been on the heap.

Native Method Stacks

It is similar to the virtual stack, except that the former serves Java methods, while the native method stack serves native methods.

Program counter (The PC Register)

Holds the address of the currently executing JVM instructions. Our program is running in a thread switch, so how do we know where the thread has been executed?

A program counter is a small memory space that acts as a line number indicator of the bytecode being executed by the current thread. And that’s the progress of the current thread.

JMM (Java Memory Model)

First of all, it is not a “real thing”, but a set of “specifications” related to multithreading, which require every JVM implementation to comply with such “specifications”. With the GUARANTEE of the JMM specification, concurrent programs running on different virtual machines are safe and reliable program results.

Without a JMM memory model to regulate it, it is possible to run with different and incorrect results after being “translated” by different JVMS.

JMM is all about processors, caches, concurrency, compilers. It solves the problem data caused by CPU multi-level cache, processor optimization, instruction rearrangement and so on, and ensures that different concurrent semantic keywords get corresponding concurrent safe data resource protection.

The main goal is to give Java programmers consistent access across platforms.

It is the principle guarantee of JUC package utility classes and concurrent keywords

Volatile, synchronized, Lock, etc., all of which are implemented based on JMM. With the JMM’s involvement, synchronization tools and keywords work and synchronization semantics work, allowing us to develop programs that are concurrency safe.

The three most important aspects of the JMM are reordering, atomicity, and memory visibility.

Instruction reordering

The bug code we wrote, I realized I was wrong when I thought it was going to run in the order I wanted it to run in. In fact, the compiler, JVM, and even THE CPU may, for performance purposes, not ensure that statements are executed in the same order as the input code, but rather adjust the order, which is called instruction reordering.

Reordering advantage

We might ask: why reorder? Have a purpose?

After reordering, the instructions for operation A are changed, saving a Load A and a Store A, reducing the execution of instructions, improving the speed and changing the operation, which is the benefit brought by reordering.

Three cases of reordering

  • Compiler optimization

    For example, if Tang Pak Hu loves Chou-heung at present, it would be much more efficient to combine his love and dating for Chou-heung. Instead of dating Chou-heung at the same time as dong-heung, this part of the time cost is reduced. At this point, we need to rearrange the order. Not reordering does not mean arbitrary sorting, it needs to ensure that after reordering, does not change the semantics of the single thread, can not transmit the words said to “Chou-heung” to “Dong Heung” ear, otherwise can arbitrary sorting, the consequences are terrible, “time management master” is only you.

  • CPU reorder

    Optimizations here are similar to compilers in that they aim to increase overall efficiency by shuffling the order, which is the secret weapon of faster execution.

  • Memory “reordering”

    I’m not really reordering, but the results are similar to reordering. I put double quotation marks around it because it’s different.

    Due to the presence of a cache in memory, which is represented in the JMM as main memory and local memory, the contents of main memory and local memory can be inconsistent, so this can also cause programs to behave out of order.

    Each thread has direct access to the working memory, not the main memory, where the data is a copy of the shared variables in the main memory. Communication between the main memory and the working memory is controlled by the JMM.

Here’s an example:

Thread 1 changes the value of A, but does not have time to write the new result back to main memory or thread 2 does not have time to read the latest value, so thread 2 cannot see the modification of THREAD 1 to A, and thread 2 still sees the original value of A. Thread 2, on the other hand, might see the code executed after thread 1 modified A, ostensibly as if a reorder had occurred.

Memory visibility

Why is there a memory visibility problem

public class Visibility {    int x = 0;    public void write() {        x = 1;    }    public void read() {        int y = x;    }}
Copy the code

Memory visibility problem: When the value of x has been modified by the first thread, but the modified value cannot be seen by other threads.

atomic

We can think of basic datatype variables, reference type variables, and accesses to any variable declared volatile as atomic (non-atomic agreements for long and double: For 64-bit data, such as long and double, the Java Memory model specification allows virtual machines to divide reads and writes to 64-bit data that are not volatile into two 32-bit operations. That is, if multiple threads share a variable of type long or double that is not declared volatile, the virtual machine implementation can choose not to guarantee the atomicity of the 64-bit load, store, read, and write operations. If they are read and modified simultaneously, some threads may read a value representing a “half variable” that is neither the original value nor the value modified by another thread.

However, since almost all commercial virtual machines on all platforms treat reads and writes of 64-bit data as atomic operations, there is no need to declare the long and double variables volatile. These variables are read and written atomically, but compound operations such as basic variable ++ / volatile++ are not. Such as i++;

Problems solved by the Java memory model

The three most important aspects of the JMM are reordering, atomicity, and memory visibility. How does the JMM solve these problems?

JMM abstracts Main Memory and Working Memory.

  • Main storage is the area where instances are located, and all instances are in main storage. For example, fields owned by an instance are in main storage, which is shared by all threads.

  • Working memory is the work area owned by threads, and each thread has its own dedicated working memory. Working memory holds copies of the necessary parts of main memory. This is called a Working Copy.

Threads cannot operate on main memory directly. As shown in the figure below, thread A can communicate with thread B only through main memory.

Eight operating

To support JMM, Java defines eight atomic actions that control the interaction between main memory and working memory:

  1. Read: Acts on main memory to transfer shared variables from main memory to the thread’s working memory for later load actions.

  2. Load: Acts on working memory and places values read from read into copy variables in working memory.

  3. Store: Applied to working memory to transfer variables from working memory to main memory for subsequent write operations.

  4. Write write: Applied to main memory to write store transfer values to variables in main memory.

  5. Use: Applied to the working memory, passing the value of the working memory to the execution engine. This action is performed when the virtual machine reaches an instruction that needs to use this variable.

  6. Assign: Applies to the working memory and assigns the values obtained by the execution engine to variables in the working memory. This operation is performed when the VM stack encounters an instruction to assign values to variables. Int I = 1;

  7. Lock acts on main memory, marking variables as thread-exclusive.

  8. The UNLOCK acts on main memory. It releases the exclusive state.

The JMM summary

The JMM is an abstract concept that defines a set of specifications to ensure ultimate concurrency security in order to mask details due to CPU multi-core and multi-level caching and instruction reordering to optimize code. It abstracts the concept of working memory over main memory, and ensures atomicity, memory visibility, and instruction rearrangement through eight atomic operations and memory barriers. Volatile ensures memory visibility and prevents instruction reordering. Synchronised ensures memory visibility, atomicity, and prevents thread-safety problems caused by instruction reordering. JMM is the foundation of concurrent programming.