For developers, it is really difficult to write good code without understanding the Java virtual machine, and it is also difficult to find good bugs, because the program we write must have bugs, so developers who are familiar with the Java virtual machine, our Java application has bugs. Troubleshooting problems with logs is easy.

JVM optimization is one of the most important questions to ask in a job interview. We are not learning to interview for the sake of interviewing, but learning the core knowledge of the JVM will be a bright spot for us in the interview process and on the job.

Think about it

When you learn something, you must know why you are learning it. Some people might say, well, it doesn’t seem like it’s useful to write code. It seems like the JVM does everything for us. Consider why you want to learn about JVM virtual machine architecture.

Do you get confused about how much heap memory to set? How is OutOfMemoryError caused? How do I tune the JVM? What is JVM garbage collection like? Even creating a String, what does the JVM do?

Memory structure diagram

When a Java virtual machine runs a Java program, the memory it manages is divided into several different data areas, including the following five parts: program counters, Java heap, Java virtual machine stack, method area, and local method stack. As shown in the figure:

The method area and heap are memory areas shared by all threads, that is, all threads can share them. The virtual machine stack, the local method stack, and the program counter are memory areas that are thread private, meaning that each thread has its own independence, or thread-isolated.

So what do these five pieces of memory do? So let’s go through them one by one.

Detailed explanation of each area

The heap

For most applications, the Java heap is the most important area of the Java Virtual machine. It is also the largest area of memory in the Java Virtual machine, where most objects are stored. The Java heap is used to store object instances and arrays, which means that objects in our code are stored there with the new keyword new. This became the main camp for the garbage collector, hence the nickname GC heap, and a single JVM process has one and only one Java heap.

The heap memory is subdivided into new generation and old generation. The specific memory structure of the Java heap is shown in the figure below:

As you can see from the figure above, the Java heap is not simply a whole region. In fact, the Java heap is divided into two regions, the young generation and the old generation, according to the difference of object lifetime. The young generation is further divided into Eden zone, From Survivor 0 zone and To Survivor 1 zone. The default VM configuration ratio is Eden: from: to = 8:1:1.

The virtual machine stack

Java virtual machine stack describes the memory model of Java method execution: each method is executed with the creation of a stack frame, each method from the call to the completion of execution process, corresponding to the process of a stack frame in the virtual machine stack.

The virtual stack is thread exclusive. When a thread is created, a virtual stack is created, and the virtual stack consists of stack frames. Each method call creates a stack frame, which is then closed. When the method returns, it corresponds to the frame out of the stack operation, in which an element is added each time the method is called. When the method returns, it pops the element.

Stack frames are used to store information such as local variables, dynamic links, operands and method exits. When a method is called, the stack frame is pushed onto the stack, and when the method call ends, the stack frame is pushed off the stack.

Local variable table: Stores basic types known at compile time, object reference types, and returnAddress types (addresses pointing to a bytecode instruction. That is, a program is a bytecode instruction stored in a method area, and a value of type returnAddress is a pointer to the memory address of a particular instruction.

 

Operand stack: Operands are a last-in, first-out stack. All of the OPERands in the JVM operate on data on the operand stack. For each method call, the JVM creates a stack of operands for calculation. The code in the method executes by copying variables or constants from the local variable table or field of the object instance and placing them in the operand stack. When evaluating, it uses a series of instructions to add or remove data from the operand stack. You can think of the operand stack as a place to store temporary data.

 

Return address: that is, the address of the next instruction after the execution of this method. When the method exits normally, the value of the caller’s PC counter can be used as the return address. When the method exits abnormally, the return address is to be determined by the exception processor.

 

Dynamic linking: When converted to a direct reference at runtime, it is called dynamic linking. Class bytecode has a large number of symbolic references in its constant holder, which are turned into direct references (that is, to data) at runtime, either to methods or fields.

 

The virtual machine stack defines two types of exceptions: StackOverFlowError (stack overflow) and OutOfMemoryError (memory overflow). StackOverFlowError is thrown if the stack depth of the thread call is greater than the maximum depth allowed by the virtual machine. However, most virtual machines allow the virtual stack to be dynamically expanded, so threads can keep requesting stacks until they run out of memory, throwing outofMemoryErrors.

Local method stack

The function of the local method stack and the virtual machine stack are similar. The virtual machine stack manages Java methods, while the Native method stack manages Native methods. Native methods are implemented by the C language area. The virtual machine specification does not enforce the language, mode, or data structure used in the local method stack. The specific content is implemented by the virtual machine. There are even virtual machines (Sun HotSpot) that simply combine the local method stack with the virtual machine stack. Like the virtual stack, the local method stack area throws StackOverflowError and OutOfMemoryError exceptions.

Program counter

A program counter is a small memory space that can be seen as a line number indicator of the bytecode being executed by the current thread. The program counter is used to record the address of the bytecode executed by each thread. Operations such as branch, loop, jump, exception, thread recovery, etc., depend on the program counter.

And you think, why do we have this program counter space? This is because Java is a multithreaded language, and when the number of threads executing exceeds the CPU core, threads compete for CPU resources on a timeslice basis. For example, if a thread’s CPU resources are preempted by another thread before it has finished executing its task, it needs to know where to proceed if it is the thread’s turn to execute, so each thread is assigned a program counter to record what instruction it will run next.

Methods area

Method areas have a lot in common with the heap: thread sharing, memory discontinuity, extensibility, garbage collection, and also throw OutofMemoryErrors when they can no longer be extended. The method area contains four sections of class information, run-time quantifiers, strings, constant pools, and static variables. The method area is used to store information about classes loaded by virtual machines.

So what does the measuring pool do? Let’s see.

Static constant pool: called the class file constant pool, it is mainly used to store literals. Literals mainly include text strings and final – modified constants. Symbolic references include fully qualified names of classes and interfaces, field names and descriptors, and method names and descriptors. This information is placed in the static constant pool.

Runtime constant pool: After a class is loaded into memory, the JVM places the contents of the static constant pool into the runtime constant pool. The runtime constant pool stores mostly compile-time literals, symbolic references, and so on.

String constant pool: A string constant pool can be thought of as a separate part of the runtime constant pool into which strings are stored when a class is loaded into memory.

It is important to note that there is an intersection between the method area and the heap. Static variables and characters are stored in the constant pool in the heap, while class information and runtime constant pool are stored in the meta-space, which is a piece of local memory.

The figure above refers to the memory distribution of JDK 8 and later, so there are actually differences in implementation between JDK distributions and even between JDK versions. For example, we are familiar with the Oracle JDK, which is a HotSpot virtual machine.

HotSpot virtual machine before JDK 7, it USES the permanent generation area to implement the method, this method area we common Java will happen. Lang. OutOfMemoryError: -xx :PermSize Sets the minimum space in the method area, and -xx :MaxPermSize sets the maximum space in the method area. In the HotSpot virtual machine prior to JDK 7, strings that were included in the string constant pool were stored in the persistent generation, resulting in a number of performance issues and memory overflow errors. A particularly striking example is the String intern () method. But other virtual machines, such as the JRockit virtual machine or IBM’s J9 virtual machine, have no permanent generation at all.

For example, in JDK 7, HotSpot virtual machine made some improvements on the original basis, including string constant pool

It was moved to the heap, and the symbol reference was put into the local inner line. Later in JDK 8, the HotSpot VIRTUAL machine just killed the permanent generation and replaced it with the meta space. In addition, the old age is separated from the meta space, which is placed in the local memory, so that the maximum space of the meta space is the memory space of the system, so that there will no longer be permanent generation memory overflow errors, and there will not be leakage of data into the swap area such things. You can set a maximum available space for the metadata space. If you do not set the maximum available space, the capacity of the metadata space is dynamically increased based on the metadata size of the class. For a 64-bit server-side JVM, the default — XX:MetaspaceSize value is 21MB. This means that the default meta-space size is 21MB.

Why has the method area changed so much since JDK1.8? There are two main reasons to use meta-spaces instead of permanent generations:

On the one hand, Oracle has acquired both HotSpot and JRockit VMS. JRockit does not have permanent generation, so in order to merge HotSpot and JRockit VMS, they have removed the permanent generation.

On the other hand: the reason is that the permanent generation is quite prone to problems in the process of use. If you are really online through the actual project, believe you have seen this exception: Java. Lang. OutOfMemoryError: PermGen. As we know, the method area was implemented with persistent generation prior to JDK 8, and the method area mainly stores a series of constants and information about classes and methods. This area can be difficult to size precisely because the classes and methods loaded by each project are different. If your permanent generation is set too small, it is easy to run out of memory in this area. If your permanent generation is set too large, it will cause memory waste.

So what are the benefits of using cloud space instead? A meta-space is a block of local memory that theoretically depends on how much memory the operating system can allocate to solve the problem of persistent space allocation. Meta-space takes up system memory, which means that the method area has enough memory as long as it doesn’t hit the system memory ceiling. But that doesn’t mean we don’t limit the method area, which would eventually crash if it expanded infinitely.

Case presentation

After exploring the JVM memory structure, let’s take a look at an example of how Java allocates storage.

 

Public class PersonTest01 {public static void main(String [] args){Person Person =new Person("itbbfx"); person.printInformation(); }} # public class Person {private String name; public Person(String name){ this.name=name; } public void printInformation(){ System.out.println(this.name); }}Copy the code

This code is very simple, creating a Person object in the main method of PersonTest01 and calling the printInformation method.

The memory distribution of the above example code is roughly as follows:

At startup, the method area of the class is loaded first. So here we need to add two classes, one is PersonTest01 and one is Person. When executing Person Person =new Person(“itbbfx”); A local variable named Person will be created and placed on the stack, which will then point to a reference. The actual object is stored in the heap, and then the printInformation method is executed.

Of course, the diagram above does not describe the details of the stack frame. In fact, for each method, there is a push and a push operation. So when you call printInformation, it creates a stack frame, goes into the stack, and when the printInformation code returns, the stack frame pops out of the station.

So here we just take a very simple example, you also need to know where static variables, constants are stored, you can refer to the previous explanation to think about it.

conclusion

In conclusion, this class analyzed the memory structure of the JVM in detail, and took you to analyze what each piece of memory does. The special one is the method area, which is not a physical area, but a logical division. This is a specification for the JVM. In JDK 8 and later, the method area is partially stored in the heap and partially stored in the meta-space.