Java’s memory management is all managed by the JVM and does not require the programmer to manage memory manually, which is programmer friendly.
But every good thing has its bad.
If there is a problem with memory management, it can be a very difficult task to troubleshoot the problem if we don’t know how the JVM manages memory.
1. Runtime data area
During the execution of Java programs, the Java VIRTUAL machine divides the memory it manages into different data areas. Each of these areas has its own purpose.
1.1 Program counter
The Program Counter Register is a small memory space that can be thought of as a line number indicator of the bytecode being executed by the current thread.
Function:
(1) Bytecode interpreter works by changing the value of this counter to select the next bytecode instruction to be executed, which is an indicator of program control flow.
(2) There is also a very important role – thread recovery, in the era of concurrency is so popular, thread context switch is common. If a thread context switch occurs, how can a thread be restored to its previous running state? You need to rely on program counters to do this.
Features:
(1) The program counter is thread private and has the same life cycle as the thread.
(2) When a Java method is executed, the value stored in the program counter is the address of the virtual machine bytecode instruction being executed; When a native method is executed (a method modified with the native keyword), the value stored in the program counter is null.
(3) Program counters are the only area where the Java Virtual Machine Specification does not specify any OutOfMemoryError cases.
1.2 Java Virtual Machine Stack
The virtual machine stack describes the thread-memory model of Java method execution.
Function:
When each method is executed, the Java VIRTUAL machine synchronously creates a Stack Frame to store information about local variables, operand stacks, dynamic connections, method exits, and so on.
The process of each method being called and executed corresponds to the process of a stack frame moving from the virtual machine stack to the virtual machine stack.
The local variable table stores various Java virtual machine basic data types, object references, and returnAddress types known at compile time.
Features:
StackOverflowError is raised if the thread requests a stack depth greater than the virtual machine allows. If the Java virtual machine stack can expand dynamically, OutOfMemoryError will be raised when sufficient memory cannot be allocated during stack expansion.
Note: The stack size of the HotSpot VIRTUAL machine is not dynamically scalable, as was the case with the Classic virtual machine.
The Java virtual machine stack is thread-private and has the same lifecycle as a thread.
1.3 Local method stack
This is similar to the Java virtual machine stack, except that the Java virtual machine stack is oriented to Java methods, whereas the native method stack is oriented to local methods.
Note: Some Java virtual machines (such as hot-Spot virtual machines) simply combine the local method stack with the virtual machine stack.
Function:
Just like the Java virtual stack, the object orientation is different.
Features:
Like the virtual stack, the local method stack throws StackOverflowError and OutOfMemoryError, respectively, when the stack depth overflows or the stack extension fails
The local method stack is thread-private and has the same lifecycle as a thread.
1.4 Java heap
It is the largest chunk of memory managed by a VM. The sole purpose of this memory area is to hold object instances, and almost all object instances in the Java world are allocated memory here.
Why almost?
Due to advances in just-in-time compilation technology, and especially the increasing power of escape analysis, on-stack allocation and scalar replacement optimization have led to subtle changes that have made it less obvious that Java object instances are all allocated on the heap.
Function:
Memory allocation for object instances, the main memory area managed by the garbage collector, is divided into regions using a generational design.
Features:
(1) The Java heap is an area of memory that is shared by all threads and is created when the virtual machine starts.
(2) The Java virtual machine will raise an OutOfMemoryError if there is no memory in the Java heap to complete the instance allocation and the heap can no longer be extended.
1.5 method area
The Method Area, like the Java heap, is an Area of memory shared by threads that stores data such as type information that has been loaded by the virtual machine, constants, static variables, and code caches compiled by the just-in-time compiler.
Note:
We usually use HotSpot virtual machine. The method area acts as a specification, and how it is implemented is defined by the virtual machine (equivalent to the relationship between interfaces and implementation classes).
- in
JDK 1.7
.HotSpot
The virtual machine implementation of the method area isThe permanent generation. - in
JDK 1.8
.HotSpot
The virtual machine implementation of the method area isdimension.
With JDK 1.8, meta-space is stored in local memory, that is, removed from the heap, not JVM memory.
The string constant pool, static variables are still in the heap, and the runtime constant pool is still in the meta-space.
Features:
OutOfMemoryError is thrown if the method area cannot meet the new memory allocation requirements.
1.6 Runtime constant pool
The Runtime Constant Pool is part of the method area.
The Constant Pool Table is used to store various literals and symbolic references generated at compile time. This part of the Table is stored in the runtime Constant Pool of the method area after the Class is loaded.
Features:
(1) Dynamic. For example, you can put values into the runtime constant pool by calling intern() of String.
(2) OutOfMemoryError will be thrown when the constant pool can no longer apply for memory.
1.7 Direct Memory
The NIO (New Input/Output) class was introduced in JDK 1.4, introducing a Channel and Buffer based I/O method that can allocate off-heap memory directly using Native libraries. This is then referenced by a DirectByteBuffer object stored in the Java heap. This can significantly improve performance in some scenarios because it avoids copying data back and forth between the Java heap and Native heap.
2. Object exploration
Take HotSpot, the most common virtual machine, and Java heap, the most common memory region.
2.1 Object Creation
The objects created here are limited to normal Java objects, not arrays and Class objects.
Because array creation does not trigger class loading.
(1) Inspection stage.
When the Java virtual machine reaches a bytecode new instruction, it first checks to see if the instruction’s arguments locate a symbolic reference to a class in the constant pool, and to see if the symbolic reference represents a class that has been loaded, parsed, and initialized. If not, the corresponding class loading process must be performed first,
(2) Memory allocation stage.
The size of the memory required by an object is fully determined after the class is loaded, and the task of allocating space for an object is essentially the same as dividing a certain size block of memory from the Java heap.
There are two ways to partition memory:
(1) Pointer collision. If memory in the Java heap is perfectly neat, all used memory is placed on one side, free memory is placed on the other, and a pointer is placed in the middle as an indicator of the dividing point, then the allocated memory simply moves that pointer to free space by an equal distance to the size of the object.
(2) Free list. But if the memory in the Java heap is not neat, has been the use of memory and free memory staggered together, that is simply no way pointer collision, the virtual machine, you must maintain a list of records on which memory blocks are available, at the time of points with from the list to find a large enough space division to the object instance, And update the records on the list. (How to find here is also a problem, can be related to its operating system memory block allocation, in the end is the first adaptation, or best adaptation or worst adaptation. I didn’t find the answer)
The choice of allocation method is determined by the cleanliness of the Java heap, which in turn is determined by the ability of the garbage collector to Compact.
- When using
Serial, ParNew
When the collector with the collation process is compressed, the system uses the allocation algorithm is pointer collision, which is simple and efficient;- And when to use
CMS
This is based on clearing (Sweep
) algorithm, in theory can only use the more complex free list to allocate memory.
Another consideration: thread safety.
Object creation is A very frequent activity in virtual machines. Even if only one pointer is changed, it is not thread-safe in concurrent situations. Object B may use the original pointer to allocate memory before the pointer is changed.
There are two solutions:
(1) CAS mode. Synchronization of memory allocation – in fact, the VIRTUAL machine uses CAS and retry to ensure atomicity of update operations;
(2) TLAB method. Each Thread allocates a small chunk of memory in the Java heap, called Thread Local Allocation Buffer (TLAB), which Thread allocates the memory. The lock is allocated in the thread’s local buffer, and only needs to be synchronized when the local buffer runs out and a new cache is allocated.
(3) Initialization stage.
The virtual machine must initialize all allocated memory space (but not object headers) to zero, which can also be done in advance of TLAB allocation if TLAB is used.
This ensures that the instance fields of the object can be used directly in Java code without assigning initial values, enabling the program to access the zero values corresponding to the data types of these fields.
(4) Perform necessary Settings.
For example, which class the Object is an instance of, how to find metadata information about the class, the Object’s hashCode(which is actually deferred until the object.hashcode () method is actually called), the Object’s GC generation age, and so on. This information is stored in the Object Header of the Object.
(5) Execute the constructor.
Object creation has just begun — the constructor, the
() method in the Class file, has not yet been executed, all fields have default zero values, and other resources and state information needed by the object have not yet been constructed as intended.
Only then can a truly usable object be fully constructed.
The source code to verify this process is available in Understanding the Java Virtual Machine.
2.2 Memory Layout of objects
The storage layout of objects in heap memory can be divided into three parts: object headers, Instance Data, and Padding.
The first part of the object is the object header:
The object header contains two types of information:
(1) The first type is used to store the runtime data of the object itself, such as HashCode, GC generation age, lock status flag, lock held by the thread, biased thread ID, biased timestamp, etc. The length of this data is 32 bits and 64 bits in 32-bit and 64-bit virtual machines (with the compression pointer turned on), and it is officially called the Mark Word.
(2) The second type is a type pointer, a pointer to an object’s type metadata that the Java virtual machine uses to determine which class the object is an instance of. and
Not all virtual machine implementations must keep type Pointers on object data; in other words, finding metadata information about an object does not have to go through the object itself. In addition, if the elephant is a Java array, it must also be in the head of an object has a piece of data is used to record the length of the array, because the virtual machine can be through ordinary Java object metadata information to determine the size of the Java object, but is not sure if the length of the array, will not be able to pass the information in the metadata deduce the size of the array.
The second part of the object is the instance data part:
The instance data part is the valid information that the object actually stores, that is, the contents of the various types of fields that we define in the program code, whether inherited from a parent class or defined in a subclass, must be recorded.
The third part of the object is the alignment fill:
It doesn’t necessarily exist, it doesn’t have a particular meaning, it just acts as a placeholder. Since the HotSpot VIRTUAL machine’s automatic memory management system requires that the object’s starting address be an integer multiple of 8 bytes, in other words, any object’s size must be an integer multiple of 8 bytes.
2.3 Object Access positioning
Objects are naturally created for later use, and our Java programs manipulate specific objects on the heap using reference data on the stack.
The main access methods are handle and direct pointer:
(1) Handle access
The Java heap may be divided into a block of memory as a handle pool. Reference stores the address of the handle of the object, and the handle contains the specific address information of the instance data and type data of the object.
(2) Direct pointer access
The memory layout of objects in the Java heap must consider how to place the information related to the access type data. The direct storage in reference is the address of the object. If only the object itself is accessed, there is no need for the overhead of an additional indirect access.
Contrast:
- The biggest advantage of using handles for access is that
reference
Is stored in the stable handle address, which only changes the instance data pointer in the handle when the object is moved (a very common behavior in garbage collection), andreference
It doesn’t need to be modified. - The biggest benefit of using direct Pointers is that they are faster. It saves the time overhead of a pointer location, since the object is accessed in
Java
Is very frequent, so this kind of overhead can add up to a very significant implementation cost.
The HotSpot virtual machine uses direct Pointers to access objects.
reference
Deep Understanding of the Java Virtual Machine by Zhiming Zhou