Written in the book of the former

Java memory regions and memory models are different things.

Memory regions: The JVM runtime stores data into regions, emphasizing the division of memory space.

Java Memory Model (JMM) : Defines the abstract relationship between threads and main Memory, that is, the JMM defines how the JVM works in computer Memory (RAM).

Memory area

Below is a map of the JVM runtime data area prior to JDK 1.8:

Below is a map of the JVM runtime data area after JDK 1.8:

Comparing the map of the JVM runtime data area before JDK 1.8 with that after JDK 1.8, we can see that 1.8 has a meta-space alternative method area. The metaspace section below explains why method areas should be replaced.

Here we describe what each region does for the JVM memory map after JDK 1.8.

Local method stack

Native Method Stacks: Provides services for Native methods that can be used by VMS. If the Native Method Stacks are available, the Native C/C++ libraries can be directly called by JNI (Java Native Interface) without the control of the JVM.

We often use Native methods to get the current time in milliseconds, which are modified by Native keywords.

package java.lang;

public final class System {
    public static native long currentTimeMillis(a);
}
Copy the code

C/C++ can, however, extend the use of Java through JNI and integrate different programming languages.

Program counter

Program Counter Register: A small memory space that acts as a line number indicator of the bytecode being executed by the current thread. Because the JVM can execute threads concurrently, each thread is assigned a program counter, the same lifetime as the thread. Therefore, there will be a switch between threads, at which point the program counter will record where the program is currently executing, so that it can resume execution after other threads have finished.

If the thread is executing a Java method, this counter records the address of the virtual machine bytecode instruction being executed. If the Native method is being executed, the counter value is null (undefined)

This memory region is the only one where the Java Virtual Machine specification does not specify any OutOfMemoryError cases.

Java virtual machine stack

Java Virtual Machine Stacks: Like program counters, the Java Virtual Machine stack is thread-private and has the same lifetime as a thread.

Virtual machine Stack describes the memory model of Java method execution: each method execution creates a Stack Frame to store information such as local variation table, operation Stack, dynamic link, method exit, etc. Each method is called until the execution is complete, corresponding to the process of a stack frame in the virtual machine stack from the stack to the stack.

A stack is a filo-first In Last Out ordered list. In an active thread, only the stack frame at the top of the stack is valid, called the current stack frame. The executing method is called the current method, and the stack frame is the basic structure in which the method runs. When the execution engine is running, all instructions can only operate on the current stack frame.

Local variable scale

A local variable table is an area where method parameters and local variables are stored. Local variables have no preparation phase and must be explicitly initialized. Global variables are placed in the heap, and there are two assignment phases, one in preparation for class loading, which gives the system its initial value; Another time, during class load initialization, the code is given the initial values defined by the class.

Stack operation

The operation stack is a bucket stack with an initial empty state (first in, then out). When a method is first executed, the operand stack of the method is empty. During the execution of the method, various bytecode instructions are written to and extracted from the operand stack.

Dynamic link

Each stack frame contains a reference to the current method in the run-time constant pool to support dynamic concatenation during method calls. If you need to call another method in the current method, you can find the corresponding symbolic reference from the runtime constant pool, convert the symbolic reference to a direct reference, and then call the corresponding method directly.

Not all method calls to dynamic linking, part of the symbol referenced in class loading phase will be symbols into direct references to this part of the operation is called: static analysis, is to compile time can determine the version of call, including: call the static method, call the instance of private constructor, a private method, the parent class method.

Method return address

There are two exits when a method executes:

  1. Normal exit, that is, normal execution of RETURN bytecode instructions to any method, such as RETURN, IRETURN, ARETURN, etc.
  2. Abnormal exit.

In any exit case, the method is returned to where it was currently called. Method exit is equivalent to popping up the current stack frame.

The heap

When we talk about GC tuning /JVM tuning, 99% of what we mean is tuning the heap! Java stacks, native method stacks, and program counters generally don’t generate garbage.

Heap: The largest chunk of memory managed by the Java virtual machine. The Java heap is an area of memory that is shared by all threads and is created when the virtual machine is started. The sole purpose of this memory area is to hold object instances, and almost all object instances are allocated memory here.

The Heap is the primary area managed by the Garbage collector, and is often referred to as the “Garbage Collected Heap.”

dimension

JDK 1.8 changed the method area to a meta-space. The meta information for a class is stored in a meta space that does not use heap memory, but is a local memory area not connected to the heap. So, in theory, the system can use as much memory, as much meta space.

Methods area

Method Area: An Area of memory shared by threads, like the Java heap, that is used to store information about classes that have been loaded by the virtual machine, constants, static variables, and code compiled by the just-in-time compiler.

Although the Java Virtual Machine specification describes the method area as a logical part of the Heap, it has an alias called non-heap, which is supposed to distinguish it from the Java Heap.

Change of method area and meta-space

JDK 1.6, JDK 1.7 to JDK 1.8 method area:

In JDK 1.8 HotSpot JVM moved out of PermGen and started with Metaspace. The main reasons for using meta-spaces instead of permanent generation implementations are as follows:

  1. Avoid OOM exceptions. Strings are stored in the permanent generation, which may cause performance problems and memory overflow.
  2. It is difficult to determine the size of permanent generation setting space. If it is too small, it is easy to overflow permanent generation, while if it is too large, it is easy to overflow old generation.
  3. Tuning permanent generation is very difficult;
  4. Merge HotSpot with JRockit;

The memory model

Memory model is to ensure the correctness (visibility, orderliness, atomicity) of shared memory. Memory model defines the specification of read and write operation of multithreaded program in shared memory system.

The memory model solves the concurrency problem in two main ways: limiting processor optimization and using memory barriers.

The Java Memory Model (JMM) controls communication between Java threads, determining when writes by one thread to a shared variable are visible to another thread.

Computer caching and cache consistency

Computers use caches between fast cpus and relatively slow storage devices as a buffer between memory and the processor. When the program is in the process of operation, the operation will be needed data from main memory physical memory (computer) to a copy of the CPU’s cache, then when the CPU calculation can directly from the cache data read and write data to it, when the operation is done, then the data in the cache refresh into main memory.

In a multi-processor system (or a single-processor, multi-core system), each processor core has its own cache, and they share the same Main Memory. When the CPU wants to read a piece of data, it first looks for it in the level-1 cache. If it doesn’t find it, it looks for it in the level-2 cache. If it still doesn’t find it, it looks for it in the Level-3 cache (not all cpus have level-3 caches) or in memory.

In a multi-core CPU, each core may have different caches for the same data in its own cache. Therefore, each processor must follow some protocols when accessing the cache, and operate according to the protocols when reading and writing the cache to maintain the consistency of the cache.

JVM main memory vs. working memory

In the Java memory model, all variables are stored in the main memory, and each thread has its own working memory. All operations on variables must be carried out in the working memory by the thread, instead of reading or writing variables in the main memory.

Working memory here is an abstraction from JMM that stores copies of shared variables that the thread reads/writes.

reorder

To improve performance, compilers and processors often reorder instructions when executing programs. There are three types of reordering:

  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 Parallelism reordering: Modern processors use instruction-level Parallelism (ILP) to overlap multiple instructions. If there is no data dependency, the processor can change the execution order of the machine instructions corresponding to the statement.
  3. Reordering of memory systems: Since the processor uses caching and read/write buffers, it can appear that load and store operations are performed out of order.

The sequence of instructions from the Java source code to the actual execution goes through one of the following three reorders:

The JMM is a language-level memory model that ensures consistent memory visibility for programmers across compilers and processor platforms by disallowing certain types of compiler reordering and processor reordering.

The Java compiler disallows processor reordering by inserting a memory barrier at the appropriate location of the generated instruction sequence (reordering cannot reorder subsequent instructions to the location before the barrier).

happens-before

The Java memory model introduces the concept of happens-before, through which memory visibility between operations is explained. By “visibility”, I mean that when one thread changes the value of the variable, the new value is immediately visible to other threads.

If the results of one operation need to be visible to another, there must be a happens-before relationship between the two operations. The two operations mentioned here can be within a thread or between different threads.

Implementation of the Java memory model

Java provides a number of keywords related to concurrent processing, such as volatile, synchronized, final, JUC packages, and so on. These are the keywords that the Java memory model provides to programmers by encapsulating the underlying implementation.

atomic

To ensure atomicity, two advanced bytecode instructions, Monitorenter and Monitorexit, are provided, which in Java correspond to synchronized.

Javap -v syncViewBytecode.class You can find the Monitorenter and Monitorexit bytecode directives.

public class SyncViewByteCode {
  public synchronized void buy(a) {
    System.out.println("buy porsche"); }}Copy the code

Bytecode, partial results are as follows:

 public com.dolphin.thread.locks.SyncViewByteCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/dolphin/thread/locks/SyncViewByteCode;

  public void test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_1
         5: monitorexit
         6: goto          14
         9: astore_2
        10: aload_1
        11: monitorexit
        12: aload_2
        13: athrow
        14: return
      Exception table:
Copy the code

visibility

The Java memory model relies on main memory as a transfer medium by synchronizing the new value back to main memory after a variable is modified and flushing the value from main memory before the variable is read.

The volatile keyword in Java provides the ability to synchronize modified variables to main memory immediately after they are modified, and to flush variables from main memory each time they are used. Therefore, volatile can be used to ensure visibility of variables in multithreaded operations.

In addition to volatile, the Java keywords synchronized and final are also visible. It’s just implemented differently.

order

In Java, synchronized and volatile can be used to ensure order between multiple threads. Implementation methods are different:

  • volatileThe: keyword disallows instruction reordering.
  • synchronizedThe: keyword ensures that only one thread can operate at a time.