preface

Before we talk about the MEMORY model of the JVM, let’s take a look at the memory processing of the physical computer. The interaction between the user’s disk and the CPU on the physical calculator, since the CPU reads and writes much faster than the disk, hence the memory (cache). However, with the development of CPU, memory read and write speed can not keep up with CPU, CPU manufacturers to each CPU to add a cache, which is the following structure.

JVM Composition parsing

  1. Runtime data area The runtime data area includes: stack, heap, method area (meta space), local method stack, program counter. Detailed concepts will be documented later.
  2. The classloading subsystem loads bytecode files into the runtime data area.
  3. Bytecode execution engine

Stack and the stack frame

In Java, every time a thread is started, the virtual machine assigns it a stack space and a program counter, which in turn contains stack frames for each method that the thread is executing. Let’s start with a simple piece of code:

public class StackDemo { public static void main(String[] args) { StackDemo sd = new StackDemo(); int number = sd.compute(); System.out.println(" +number "); } public int compute(){ int a = 1; int b = 2; int c = (a + b) * 10; return c; }}Copy the code

Decompile the generated class file to generate the corresponding JVM instruction code:

The javap -c stackdemo. class command decompiles the class file and will decompile it, printing the instructions directly to the console.

public class com.jdc.demo.StackDemo { public com.jdc.demo.StackDemo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class com/jdc/demo/StackDemo 3: dup 4: invokespecial #3 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method compute:()I 12: istore_2 13: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 16: new #6 // class java/lang/StringBuilder 19: dup 20: "<init>":()V 23: LDC #8 // String invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder; 28: iload_2 29: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 32: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 38: return public int compute(); Code: 0: iconst_1 // pushes the constant 1 of type int onto the operand stack. 2: iconst_2 // Pushes int constant 2 onto the operand stack. 4: iloAD_1 // Load int value from local variable 1 onto the stack. 5: iload_2 // loads int values from local variable 2 onto the stack. 6: iadd // Add the two ints at the top of the stack. 7: bipush 10 9: imul // multiply two int numbers at the top of the stack. 11: iload_3 // Load int from local variable 3(c) onto the stack. 12: iReturn // return}Copy the code

This is a simple StackDemo class, which can be interpreted by checking the instructions provided by Oracle. There are many online. Here I’ve annotated the meaning of the instructions used by the compute() method. Take a look at the execution in the comments for the compute() method.

Let’s look at the execution process in combination with the diagram:

FILO There are two methods in the main thread, main() and compute(), which do the following:Copy the code
  1. After the main thread starts, it executes the main() method, which creates a new block of stack space (stack frame).
  2. Local variables created inside the main() method are stored in this stack frame. But note that if the local variable is an object, its value is not stored in the local variable table, but in the heap, which points to the corresponding address in the heap. As you can see from Figure 1 in the first section, there are many object references to the heap in the stack.
  3. When main() gets to the point where compute() is called, the thread calls compute() and compute() goes on the stack, again assigning it a stack frame to hold its own local variables.
  4. After the compute() method has finished executing its logic, compute() exits the stack
  5. At this point it returns and continues with the next operation in the main() method.

Local variable scale

As can be seen from the above section figure and the execution process, the local variable table and operand stack cooperate to complete the operation of data processing. For example int a=1, the instruction code is divided into three steps:

  • So if I put this 1 on the operand stack,
  • At the same time, a small space is applied for to store the variable A in the local variable table.
  • The assignment is then completed by pushing 1 off the stack from the operand and assigning to A in the local variable table.

The operand stack

As can be seen in the diagram of stack and in the execution process, the local variable table and operand stack cooperate to complete the operation of data processing. For example int a=1, the instruction code is divided into three steps:

  • So if I put this 1 on the operand stack,
  • At the same time, a small space is applied for to store the variable A in the local variable table.
  • The assignment is then completed by pushing 1 off the stack from the operand and assigning to A in the local variable table.

Dynamic link

Dynamic link: the method is stored in the method area, the method is loaded into the method area of the corresponding entry memory address (when other methods call) through dynamic link can be very convenient to know the corresponding method code in the method area address.

Methods the export

Compute () returns to main(), where execution continues from the next line that calls compute() from main(), rather than starting from the first line of main().

Program counter

Program counter, very simple but very important a design.

Each thread starts with a program counter, as shown in the JVM instruction code generated in the stack Frame section, with the numbers 0, 1, 2… This value is given to the program counter.

Why program counters?

Program counter functions: Because Java is multithreaded execution, so there is a thread priority of high and low points, if in the process of this thread, there is a higher priority thread preemption CPU resources, such as the highest priority thread execution is completed, then the CPU resources to the current thread, the current thread is through the program counter to know it to which step.

Heap memory

Heap memory is the most important and complex part, which is responsible not only for creating new objects, but also for GC. One of the important indicators to judge the performance of a system is how the programmer manages the heap memory. Because most JVM tuning refers to heap memory.

Let’s start with the structure of heap memory:

1. All created objects will be placed in the Eden area of the young generation. When the Eden area is full, the virtual machine will perform GC, but the GC is not full but minor. It is important to mention the GCRoots root node (local variables in the thread stack, static variables, variables in the local method stack, etc.). When gc is needed, the JVM will look for references below each GCRoots root node in Eden, layer by layer, until the last object has no other references. The virtual machine treats all objects in the process as non-garbage.

2. During GC, these non-garbage objects will be assigned to S1, and the remaining garbage objects that are not referenced in Eden will be cleaned up. After cleaning, Eden will be empty, and useful objects will be stored in S1, and then S1 and S2 will be replaced (Eden and S1 were used together before, The next GC is Eden and S2. The second gc will perform the same operations for Eden and S1 as the first GC… After each gc, the age of the surviving object is +1, and after a certain number of GC, that is, the survivor object is old enough for the virtual machine to place it in the old age. ——-> View JVisualVM using Java built-in tools

3. When the old area is full, the JVM will perform a very time-consuming FULL GC, at which time the program cannot continue. When the full GC is finished, if it goes well, the program will continue to execute, but with some performance loss. Because the usual talk about JVM tuning is reducing the number of gc’s and the time per GC’s (you can set the initial heap size… And so on), if the current objects in the old era are all non-garbage objects, then OOM memory overflow occurs.

Mention the JVM’s garbage collection algorithm

Garbage collection algorithm has four kinds, one by one to introduce: 1, mark – remove: the most basic is also the simplest, the most easy to implement an algorithm, divided into two stages, the first stage has no reference to mark the object (garbage), the second stage to remove. Advantages: convenient. Disadvantages: Memory fragmentation, because you don’t know what is garbage and what is not garbage on a piece of memory, so there will be a lot of memory fragmentation after cleaning.

2. Replication algorithm: it is a method still used by the new generation, but optimized in current use (8:1:1). It is implemented by dividing a block of memory into two equally sized pieces, using one piece at a time, and when the block is full, moving the remaining objects on it to the other block… Recycle. Disadvantages: insufficient use of memory, spend 100 yuan, but can only enjoy 50 yuan of service.

3. Mark-collation: This algorithm is similar to the first kind of mark clearing, which is the first stage of the garbage object is marked, but the second stage of mark collation is not immediately cleaned up, but the surviving objects are moved to the side, and then finally clean up the memory of the garbage object.

4. Generational collection: At present, THE MOST commonly used JVM is copying algorithm for the new generation, while marking sorting algorithm is used for the old. Because the new generation is generating objects all the time, it is very easy to fill up, which means that it needs to be cleaned up frequently, so it uses the copy algorithm, but its copy algorithm does not mean that the memory is divided into two 1:1 pieces, but the default is 8:1:1

You can look at the memory changes using The JVisualVM that comes with Java

Here is only one diagram attached, because the monitoring above it is dynamic change, and the theoretical record has been cleared, interested partners can try. Code:

public class Test{ public static void main(String[] args) throws Exception { List<Test> list = new ArrayList<Test>(); While (true){list.add(new Test()); Thread.sleep(10); }}}Copy the code

Win + R – > CMD – > jvisualvmAbove:

A word about JVM tuning

STW: Stop the word means that during the gc process, the program thread is suspended, which depends on whether there is too much garbage. If there is too much garbage, the GC will take a long time to execute, and the user experience will be very bad. JVM tuning: Reduce STW times and times, but STW is a must; it’s Java’s design mechanism. Like program counters, very clever.

The reason: As you can see from the first figure of this article, taking the stack and heap as an example, the method on the stack has a reference to an object, and the reference refers to the object in the heap. When gc is performed, the GCRoots root is used to find the heap step by step and then the next reference from the heap, such as in a project. Objects are created continuously (a few seconds before a big promotion grab in an e-commerce project), in which the object created per second may be tens of MB, plus the associated order object creation, shopping cart, etc. If this time I gc, gc is need time, its time and STW time, but without STW, I was in before the start of the gc, and tens of thousands of objects are created in the process of (this time they have references, such as creating order depend on the shopping cart object), gc completed at this time, After the stack frame is released, there is no local variable table for the method, and since I start by looking for references from an object in the local variable table as the GCRoots root, if there is no STW time, my object would not be garbage after the GC execution. But it now has no GCRoots root, naturally no reference, at this time my object becomes garbage, is not used to become garbage, then the program will certainly GG…

Methods area

It mainly stores constants, static variables and class information.

Local method stack

Methods to execute non-Java native code (native keywords).

The last

Thank you for reading here, the article has any shortcomings please correct, feel the article is helpful to you remember to give me a thumbs up, every day will share Java related technical articles or industry information, welcome to pay attention to and forward the article!