Java memory allocation is a very important part of Java fundamentals, and an in-depth understanding of the JVM’s memory allocation mechanism will help us solve many of the problems we encounter in development. At the same time, memory allocation is also a frequent interview area. As an interviewer, I’ve asked a lot of questions about memory allocation, but I’ve been surprised to find that three or five years of experience have given me very vague answers about this topic. In this section we take a systematic look at the memory allocation mechanism in Java.

Overview of JVM memory allocation

First of all, we should understand that JVM is not a single existence. In the 20-year history of Java, there have been many excellent virtual machines, such as HotSpot VIRTUAL machine is one of them. Memory allocation varies slightly from virtual machine to virtual machine, but generally follows the Java Virtual Machine Specification. In the Java Virtual Machine Specification, there are five types of virtual machine run-time data areas: program counter, Java virtual machine stack, local method stack, local method area, Java heap, and method area. As shown below:

Next, let’s analyze the above five memory regions in detail.

1. Program Counter

A program counter is a block of memory used to store the address of the unit where the next instruction resides. In Intel x86 and Itanium microprocessors, it is called a Instruction pointer (IP). In the JVM, a program counter is a small memory space that can be viewed as a line number indicator of the bytecode being executed by the current thread. The bytecode interpreter works by changing the value of this counter to select the next bytecode instruction to be executed. Branches, loops, jumps, exception handling, thread recovery and other functions depend on this counter.

We know that under the condition of multiple threads, thread is performed by the CPU scheduling to the directive, in a single core CPU, there would be only one thread at a certain moment in the instruction execution, which means that the CPU will frequently switch threads, and need to recover after the thread to the execution of the right position, which requires each thread has an independent program counter, Counters do not affect each other between threads.

Also, one thing to know about program counters is that the Java Virtual Machine specification does not specify any OutmemoryErrors for program counters. And why? I personally think it’s the small memory footprint of the program counter and the limited number of threads. Therefore, there is no restriction on it in the specification. From the above we can conclude the following points:

  • Program counters are thread private; each thread has one program counter.
  • The program counter holds the location of the next instruction to be executed
  • The bytecode interpreter executes instructions by changing the value of the counter
  • The program counter is the only area of JVM memory where outMemoryErrors are not specified

Java Virtual Machine Stacks

Stack memory is used to store references to basic data types and objects. But is this the case? Actually, not exactly! This is just part of the functionality of the Java virtual machine stack.

The virtual stack describes the memory model of Java method execution: each method execution creates a stack frame to store information about local variables, operand stacks, dynamic connections, method exits, and so on. The process of each method from invocation to completion corresponds to the process of a stack frame being pushed into and out of the virtual machine stack.

Stack frame is a data structure used to support virtual machine method call and method execution. It is the stack element of virtual machine stack in the data area during virtual machine operation. Stack frame stores the information of method variation table, operand stack, dynamic connection and method return. The contents of this article do not do too much interpretation of the stack frame, you can understand.

What we usually refer to as “stack memory” is actually the local variable scale part of the virtual machine. The local variable table holds all the basic data types (Boolean, type, char, short, int, final, long, double) that the compiler can know about, object references (reference types, which may be a reference pointer to the starting address of the object, They can also point to a handle representing an object or other location associated with that object) and the returnAddress type (which points to a bytecode instruction address). Data of length long and double occupy two local variable Spaces (slots), and the rest occupy only one. The space needed for the local variable table is allocated at compile time. When entering a method, how much local variable space the method needs to allocate in the frame is completely determined, and the local variable size does not change during the method’s run.

At the same time, the Java virtual machine stack, like the program counter, is thread private. The Java virtual machine stack has the same life cycle as a thread. In addition, the Java VIRTUAL Machine specification specifies two exceptions for this area: a StackOverflowError is thrown if the stack depth of a thread request is greater than the depth allowed by the virtual machine; An OutOfMemoryError is raised if the virtual stack can be dynamically expanded (as most virtual machines currently can) if sufficient memory cannot be allocated during the expansion.

From the above, we can draw the following conclusions:

  • The virtual machine stack is thread-private and has the same lifetime as the thread
  • The virtual machine stack does more than store references to basic data types and objects
  • The virtual machine stack describes the in-memory model of Java method execution
  • StackOverflowError and OutOfMemoryError are thrown when the Java virtual machine stack is out of memory

Native Method Stack

The function of the Native method stack is very similar to that of the virtual machine stack, except that the virtual machine stack performs Java method (bytecode) services for the virtual machine, while the Native method stack serves the Native methods used by the virtual machine. The virtual machine specification does not enforce the language, usage or data structure of methods in the local method stack, so the virtual machine is free to implement. The HotSpot VIRTUAL machine, for example, combines the local method stack with the virtual machine stack.

Like the virtual machine stack, the local method stack area throws stackOverflowErrors and OutofMemoryErrors.

For the local virtual stack, we have the following summary:

  • The local method stack serves the Native methods used by the VIRTUAL machine
  • Some virtual machine local method stacks are combined with virtual machine stacks
  • StackOverflowError and OutOfMemoryError are thrown in the local method stack area

Fourth, the Java heap

Heap memory is probably the most familiar area of memory for Java developers because it is so important! The object instances we create are almost always stored in heap memory. Therefore, the Java heap is the largest area of memory managed by the 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. To heap memory in the Java virtual machine specification have such description: all the object instance and array on the heap allocation, but with the development of the JIT compiler and escape analysis technology mature gradually, stack allocation, replacement scalar optimization technology will lead to some subtle changes, all objects are allocated on the heap is not absolute.

There is also a breakdown of Java heap memory:Young Generation,The Old Generation); The new generation can be subdivided into Eden space, From Survivor space and To Survivor space. Permanent Generation is not part of the heap memory, but part of the method section.Garbage collection in Java, on the other hand, is about scanning and collecting different areas of the Java heap, which I’ll explain in more detail in a follow-up article on Java’s GC mechanism.

From the perspective of memory Allocation, it is possible to allocate Thread Local Allocation Buffers (TLabs) in the Java heap shared by threads.

However, whatever is on the heap is an instance of an object. Further partitioning is simply for better memory reclamation or faster memory allocation.

In addition, the Java Virtual Machine specification states that the Java heap can be in a physically discontinuous memory space. OutOfMemoryError is thrown if the virtual machine does not have enough space to allocate memory for the instance and the heap is not expanding.

The Java heap can be summarized as follows:

  • Heap memory is an area of memory shared by all threads
  • Almost all objects created in Java are stored in heap memory
  • The garbage collection mechanism scans heap memory and collects garbage objects
  • An OutOfMemoryError is thrown when the heap is out of memory

5. Method Area

The method area is used to store virtual machine-loaded class information, constants, static variables, just-in-time compiler compiled code, and so on. The method area, like the heap memory, is an area shared by all threads. Java virtual machines have very loose restrictions on method areas, which results in different virtual machine method areas behaving differently. Take the HotSpot VIRTUAL machine as an example: The method area was a separate area prior to JDK1.7, and many people liked to call it Permanent Generation, but essentially the two are not equivalent, simply because the Design team of the HotSpot VIRTUAL machine extended GC Generation collection to the method area, or implemented the method area using Permanent Generation. This allows HotSpot’s garbage collector to manage this memory as well as the Java heap. However, for other virtual machines (BEA JRockit, IBM J9, etc.) there is no concept of permanent generation. It is not a good idea to implement method areas with persistent generation because it is more likely to run out of memory. The HotSpot team is clearly aware of this and moved the string constant pool in the method area to heap memory in JDK1.7, and abolished “persistent generation” completely in JDK1.8, replacing persistent generation with meta-space.

Also, according to the Java Virtual Machine specification, OutOfMemoryError is thrown when the method area cannot meet memory allocation requirements.

The method area is summarized as follows:

  • The method area, like the heap memory, is an area shared by all threads
  • The method area is used to store virtual machine-loaded class information, constants, static variables, just-in-time compiler compiled code, and so on
  • Different virtual machines implement the method area differently
  • The HotSpot virtual machine moved the string constant pool to heap memory in JDK1.7 and removed the “permanent generation” in JDK1.8 with meta space.
  • An OutOfMemoryError is thrown when the method area is out of memory

Constant pools in Java

Java can be divided into three types of constant pool, respectively: global string constant pool, Class file constant pool, runtime constant pool. Many uninitiated developers tend to confuse these three areas. In this section, we will analyze the three types of constant pools in detail.

1. String Pool (String Pool)

We know that strings are reference data types, but strings are one of the most frequently used data types in Java. Therefore, to save program memory and improve performance, Java designers created an area called the String constant pool to store some global strings. The string constant pool is a space common to all classes. There is only one constant pool area in a virtual machine. After the class is loaded, validated, and ready, the string object instance is generated in the heap and the reference value of the string object instance is stored in the string constant pool (the description here refers to JDK7 and later HotSpot VIRTUAL machines). The string constant pool in the HotSpot virtual machine is implemented through a StringTable class. It is a hash table that holds string references (JDK6 and previous versions hold string objects). We mentioned in the previous section that before JDK7, the string constant pool was in the method area (permanent generation), where string objects were stored in the constant pool. In JDK7, the string constant pool is moved from the method area to the heap, and string objects are stored in the heap. Only references to string objects are stored in the string constant pool.

2.Class Constant Pool

As we know, Java files will generate Class files when compiled by JavAC. In addition to the description of Class version, fields, methods, interfaces, etc., there is also a constant Pool Table. The constant pool is used to hold the various literal and symbolic references produced during compilation. The Class file constant pool will not be covered in this article, but if you want to learn more about the Class file constant pool, please refer to Chapter 6 of Understanding the Java Virtual Machine by Zhiming Zhou.

3. Runtime Constant Pool

The runtime constant pool is the part of the method area into which the virtual machine loads the contents of the Class file constant pool after the Class is loaded. Therefore, each class has a pool of runtime constants. The runtime constant pool is dynamic relative to the Class file constant pool. Because the Java language does not require constants to be generated at compile time. That is, the contents of the Class file constant pool are not preset to enter the runtime constant pool, and new constants can be added to the constant pool at runtime. For example, the intern() method in the String class has the same memory limit as the method area because the runtime constant pool belongs to the method area. An OutMemoryError is raised when the constant pool is no longer allocated to memory.

conclusion

This article examines the JVM memory allocation strategy in detail. There are numerous articles on the web about JVM memory allocation. But I can guarantee that most articles are not as detailed and easy to understand as this one. If you read this article carefully, you will have a better understanding of memory allocation for the JVM.

Reference & Recommended reading

In-depth Understanding of the Java Virtual Machine third edition by Zhiming Zhou

A distinction between several constant pools in Java