JVM Memory Model (Java Virtual Machine,JVM)

Java VIRTUAL Machine JVM = Classloader + Execution Engine + Runtime data area

** 1, Program Counter Register **

A program counter is a small memory space that serves:

1.1. Can be seen as a signal indicator of the bytecode being executed by the current thread. The bytecode interpreter selects the next bytecode instruction to be executed by changing the value of the counter. Branch, loop, jump, exception handling, thread recovery and other basic functions depend on the counter to complete. Note: However, if the current thread is executing a local method, the program counter is empty.

1.2. In the case of multiple threads, the program counter is used to keep track of where the current thread is executing, so that when the thread is switched back it can know where it was last run. Features: Threads are private, and the life cycle is created with the creation of a thread and dies with the end of the thread. This memory region is the only one where the Java Virtual Machine specification does not specify any OutOfMemoryError cases.

** 2. Java Virtual Machine Stack **

The Java virtual machine stack, like the program counter, is thread-private and has the same lifetime as the thread. The virtual machine stack describes the memory model of Java method execution: each method execution creates a StackFrame to store information about local variables, operand stacks, dynamic links, method exits, and so on. Each method called until it is executed corresponds to a stack frame moving from the virtual machine stack to the virtual machine stack. The local variable table holds various basic data types (Boolean, byte, CHAR, short, int, float, long, double), object references (reference types), and returnAddresses that are known at compile time Type (refers to the address of a bytecode instruction). 64-bit longs and doubles take up two local variables (Slot, one 32-bit) and only one for the rest of the data types. The space needed for the local variable table is allocated at compile time. When entering a method, it is determined how much local variable space it needs to allocate in the frame. The size of the local variable table does not change while the method is running. The local variable table is created as the stack frame is created when the method is executed. In addition, the size of the local variable table is determined at compile time, and only a predetermined size is assigned at creation time. In addition, the size of the local variable scale does not change during the operation of the method.

The Java Virtual Machine specification specifies two exceptions for this zone:

2.1. If the depth of the line request is greater than the allowable depth of the virtual machine, stack overflow, such as StackOverflowError is thrown in recursion.

2.2. An OutOfMemoryError is raised when sufficient memory cannot be allocated for vm stack dynamic expansion. When a method passes an argument, it actually passes a copy of the local variable table in its stack frame to the local variable table in another method’s stack frame, regardless of the data type (base type, reference type).

3. Native Method Stack

Java virtual machines may use traditional stacks to support the execution of native methods (methods written in languages other than the Java language). Thread-private virtual machines such as Sun HotSpot directly combine the local method stack and the virtual machine stack into one.

The Java Virtual Machine specification specifies two exceptions for this zone:

3.1. Throw StackOverflowError if the thread request is deeper than the virtual machine allows.

3.2. An OutOfMemoryError is raised when sufficient memory cannot be allocated for vm stack dynamic expansion.

4. Java Heap

The Java heap is the largest chunk of Java Virtual machine managed memory, an area of memory shared by all threads and created with virtual machine startup. The only purpose of this section is to hold object instances, almost all of which are allocated in the heap. The Java Virtual Machine specification states that the Java heap can exist in a physically discontinuous memory space, as long as it is logically continuous. Like disk space, the Java heap can be either fixed size or extended, which is currently implemented by the majority of virtual machines (controlled by -xmx and -xMS). The Java heap is the main area managed by the garbage collector, so it is also called the “GC heap “. The new generation can be divided into Eden space, From Survivor space and ToSurvivor space. The Java Virtual Machine specification specifies an OutOfMemoryError for this area: An OutOfMemoryError is thrown if there is no memory in the heap to complete the instance allocation and the heap can no longer expand. (When Ol D area is Full, perform Full GC. After Full GC, if Survivor and old area still cannot store some objects copied from Eden, OOM error will occur/or store large objects and large arrays directly, resulting in insufficient space of old age)

5. Method Area

The method area, like the Java heap, is an area of memory shared by threads to store information about classes that have been loaded by the virtual machine, constants, static variables, code compiled by the just-in-time compiler, and so on. In HotSpot, method areas are implemented with persistent generation, whereas other virtual machines (BEA JRockit, IBM J9, etc.) do not have persistent generation. Runtime constant pools have been removed from the permanent generation in Java7, creating an area in the Java Heap for runtime constant pools. In Java8, however, persistent generation is completely eliminated, and the method area is placed directly in a region of local memory not connected to the heap, called the meta-space. Similar in nature to permanent generations, meta-spaces are implementations of method areas in the JVM specification. However, the biggest difference between a meta-space and a permanent generation is that the meta-space is not in the virtual machine, but uses local memory. Therefore, by default, the size of the metasapace is limited only by local memory, but you can specify the size of the metasapace with the following argument: -xx :MetaspaceSize, the initial size of the metasapace. -xx :MaxMetaspaceSize. The maximum space is unlimited by default. The Java Virtual Machine specification specifies OutOfMemoryError for the method area: If the memory space of the method area does not meet the memory allocation request, the Java virtual machine will throw an OutOfMemoryError.

Runtime Constant Pool

The runtime constant pool is part of the method area. Thread sharing. In addition to the Class version, field, method, interface, and other information, the Class file contains the constant pool, which is used to store various literal constants and symbolic quotes generated at compile time. This part of the content is stored in the constant pool of the method area after the Class is loaded. Static variables modified static variables are also stored in the method area, but not in the constant pool (local variables cannot be modified). Static variables cannot be defined inside a method (final can be), only as member variables. When the class is loaded by the Java Virtual machine, the constants in the class file are stored in the runtime constant pool in the methods area. And at run time, new constants can be added to the constant pool. For example, the Intern () method of the String class adds String constants to the constant pool at run time. When some constants in the run-time constant pool are not referenced by objects and are not referenced by variables, then the garbage collector needs to collect them. The Java Virtual Machine specification specifies OutOfMemoryError for this area: An OutOfMemoryError is thrown when the constant pool cannot claim memory.

7. Direct memory

Direct memory is memory other than that of the Java virtual machine, but may also be used by Java. NIO introduced a channel – and buffer-based IO approach. It can directly allocate memory outside of the Java VIRTUAL machine by calling local methods, and then manipulate that memory directly through a DirectByteBuffer object stored in the Java heap, without copying data from outside memory to the heap, thus improving the efficiency of data manipulation. The size of direct memory is not controlled by the Java VIRTUAL machine, but since it is memory, an OOM exception will be raised when the memory is insufficient.

8, stack frame

A stack frame is a data structure used to support virtual machine method invocation and method execution. It is the stack element of the virtual machine stack in the data area when the virtual machine is running. The stack frame stores information about a method’s local variator, operand stack, dynamic linkage, and method return address. The first method corresponds to the process of a stack frame in the virtual machine stack from the beginning of the call to the completion of execution. At the time of the compiled Code, how much local variables in the stack frame, how deep the operand stack are completely determine, and write Code attributes the method table, so a stack frame needs to how much memory allocation, not affected by the process sequence run-time variable data, but only depends on the specific implementation of the virtual machine. The chain of method calls in a thread can be long, with many methods handling execution state simultaneously. For the execution engine, only the Stack Frame at the top of the virtual machine Stack is valid in the active thread, called the Current Stack Frame, and the Method associated with this Stack Frame is called the Current Method. 8.1. Local variation scale

Java Memory Model (JMM)

It is a mechanism and specification that conforms to the memory model specification, shielding the access difference of various hardware and operating system, and ensuring that the Java program can get the same effect on the memory access under various platforms. The goal is to solve atomicity, visibility (cache consistency), and orderliness problems due to multiple threads communicating through shared memory.

Basic Concepts:

1. Atomicity

Thread is the basic unit of CPU scheduling. The CPU has the concept of time slice and will schedule threads according to different scheduling algorithms. So in a multithreaded scenario, atomicity problems occur. When a thread performs a read rewrite operation, it is required to abandon the CPU and wait for rescheduling after the time slice runs out. In this case, read rewriting is not an atomic operation. There is a problem with atomicity.

2. Cache consistency

In a multi-core CPU, multi-threaded scenario, each core has at least one L1 cache. If multiple threads access a shared memory in a process and execute on different cores, each core keeps a buffer of shared memory in its own CAEHE. Since multiple cores can be parallel, it is possible for multiple threads to write to their caches at the same time, and the data in their caches may be different. Adding caches between CPU and main memory can lead to cache consistency issues in multi-threaded scenarios, that is, in a multi-core CPU, each core’s own cache may have different contents for the same data.

3. Orderliness

In addition to the introduction of time slices, due to processor optimization and instruction rearrangement, the CPU may also execute input code out of order, such as load->add->save may be optimized to load->save-> Add. That’s the problem of order. The consistency problems caused by multi-CPU and multi-level caching, the atomicity problems caused by THE CPU time slice mechanism, and the ordering problems caused by processor optimization and instruction rearrangement are all caused by the continuous upgrading of hardware. So, is there a good mechanism to solve these problems? The simplest and most straightforward way to do this is to do away with the processor and processor optimization techniques, do away with CPU caching, and let the CPU interact directly with main memory. However, doing so can guarantee concurrency problems in multiple threads. But that’s a bit of throwing out the baby with the bath water. Therefore, in order to ensure that concurrent programming can meet the atomicity, visibility and order. There is an important concept, and that is the memory model. In order to ensure the correctness (visibility, orderliness, atomicity) of shared memory, the memory model defines the specification of read and write operation of multithreaded program in shared memory system. These rules are used to regulate the read and write operations of memory, so as to ensure the correctness of instruction execution. It’s about the processor, it’s about the cache, it’s about concurrency, it’s about the compiler. It solves the memory access problems caused by multi-level CPU cache, processor optimization and instruction rearrangement, and ensures the consistency, atomicity and order in concurrent scenarios.

For these problems, different operating systems have different solutions, and the Java language in order to shield the underlying differences, defined a set of Java language memory model specification, namely the Java Memory model. The Java memory model stipulates that all variables are stored in the main memory, and each thread has its own working memory. The working memory of the thread stores a copy of the main memory of the variables used in the thread. All operations on variables must be carried out in the working memory of the thread, instead of reading and writing the main memory directly. Different threads cannot directly access variables in each other’s working memory, and the transfer of variables between threads requires data synchronization between their own working memory and main memory. The JMM is used to synchronize data between working memory and main memory. It specifies how and when to synchronize data. 

Implementation of the Java memory model

Those of you familiar with Java multithreading know that Java provides a series of keywords related to concurrent processing, such as volatile, synchronized, final, concurren packages, and so on. These are the keywords that the Java memory model provides to programmers by encapsulating the underlying implementation. When developing multithreaded code, we can directly use keywords like synchronized to control concurrency and never need to worry about underlying compiler optimizations, cache consistency, and so on. Therefore, the Java memory model, in addition to defining a set of specifications, provides a set of primitives that encapsulate the underlying implementation for developers to use directly. This article does not attempt to cover the use of all keywords individually, as there is much information available on the web about the use of individual keywords. Readers can learn for themselves. Another important point of this article is that, as we mentioned earlier, concurrent programming should solve the problems of atomicity, orderliness, and consistency. Let’s take a look at the methods used to ensure this in Java.

Atomicity: In Java, two high-level bytecode instructions, Monitorenter and Monitorexit, are provided to ensure atomicity. Synchronized is the key word corresponding to these two bytecodes in Java. Therefore, synchronized can be used in Java to ensure that operations within methods and code blocks are atomic.

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 in a different way. It’s not expanded anymore.

Orderliness: In Java, synchronized and volatile are used to ensure orderliness between multiple threads. The implementation is different: the volatile keyword disallows instruction reordering. The synchronized keyword ensures that only one thread can operate at a time. Ok, so this is a brief introduction to the keywords that can be used to solve atomicity, visibility, and order in Java concurrent programming. As readers may have noticed, the synchronized keyword seems to be all-purpose, satisfying all three of these attributes at once, which is why so many people abuse synchronized. Synchronized, however, is a performance deterrent, and while the compiler provides many lock optimization techniques, overuse is not recommended.

How to answer an interview

When an interviewer asks you, “Can you explain briefly what you understand about the memory model?”

First, check with the interviewer: By memory model, you mean the JMM, which is related to concurrent programming. After you get a positive response, start the introduction (if not, you may need to answer the JVM heap/stack/method area…. Sorry…). : Java memory model, in fact, is to ensure that Java programs in various platforms to access memory can get the same effect mechanism and specification. The goal is to solve atomicity, visibility (cache consistency), and orderliness problems due to multiple threads communicating through shared memory. In addition, the Java memory model provides a set of primitives that encapsulate the underlying implementation for developers to use directly. Some of the keywords we use are synchronized, volatile, and synchronized packets. This is the end of your answer, and then the interviewer may continue to ask questions, and then continue to answer according to his question.