From: juejin. Cn/post / 684490…
Welcome to my GitHub:
Github.com/hsfxuebao/j…, hope to help you, if you feel ok, please click on the Star
preface
JVM memory areas include PC counters, Java virtual machine stacks, local method stacks, heaps, method areas, runtime constant pools, and direct memory.
This paper mainly introduces the functions and characteristics of each memory region, as well as the possibility and exception types of memory overflow in each region.
The body of the
(I). JVM memory area
When a Java VM executes Java programs, it divides the managed memory into different data areas. Each of these memory areas has its own purpose and creation and destruction time. Some regions exist with the start of a virtual machine process, and some regions are created and destroyed with the start and end of a user thread.
The JVM memory region is also called the Java runtime data region. These include: program counters, virtual stack, local method stack, heap, static method area, static constant pool, and so on.
Note: program counters, virtual machine stacks, and local method stacks are private to each thread; The heap and method areas are shared by threads.
1.1. PC counter
The Program Counter Register is a small memory space that acts as an indicator of the bytecode line number executed by the current thread.
- An indicator of the bytecode line number executed by the current thread.
- Each thread has its own
PC
Counter. - threadprivate, the life cycle is the same as the thread, along with
JVM
Start to be born,JVM
Shut down and die. - Threads execute
Java
Method, record the virtual machine it is executingAddress of bytecode instruction. - Threads execute
Native
Method, the counter is recorded asempty(Undefined
). - The only on
Java
The virtual machine specification does not specify anyOutOfMemoryError
Situation area.
1.2. Java Virtual machine stack
Thread private memory space, which has the same life cycle as a thread. During thread execution, a Stack Frame is created for each method execution, which is used to store information about local variables, operand stacks, dynamic links, method exits, and so on.
- Local variable scale
- The operand stack
- Dynamic link
- Methods the export
The process of each method from invocation to completion corresponds to the whole process of loading and unloading a stack frame in the virtual machine stack.
The concrete structure and function of the four components in the stack frame are explained as follows:
1). Local variation scale
A local variable table is a storage space for a set of variable values used to store method parameters and local variables. Max_locals in the Code property of the method table of the Class file specifies the maximum size of the local variable table required by the method.
The local variable table allocates memory space at compile time and can hold various variable types at compile time:
- Basic data types :
boolean
.byte
.char
.short
.int
.float
.long
.double
Etc.8
Kind of; - Object reference type :
reference
, pointing to an objectThe starting addresstheReference to a pointer; - Return address type :
returnAddress
, returns the type of the address.
Variable Slot:
A variable slot is the smallest unit of a local variable scale, defined as 32 bits. For 64-bit long and double variables, the virtual machine allocates two consecutive Slot Spaces.
2) operand stack
An Operand Stack, also known as an operation Stack, is a last-in, first-out Stack. Max_stacks in the Code attribute of the Class file specifies the maximum stack depth during execution. The interpreted execution engine of the Java virtual machine is called a stack-based execution engine, where the stack refers to the -operand stack.
- andLocal variable scaleThe same,The operand stackIs also an example of
32
Word lengthIs an array of units. - The virtual machine is stored in the operand stackThe data type:
int
,long
,float
,double
,reference
andreturnType
And other types (forbyte
,short
As well aschar
Values of type are also converted to before being pushed onto the operand stackint
). - Unlike a local variable table, it is accessed not through an index, but through the standard stack operations – pushing and unloading. For example, if an instruction pushes a value onto the operand stack, another instruction can pop that value up for use later.
The virtual machine uses the operand stack as its workspace — most instructions pop data from here, perform operations, and then push the results back into the operand stack.
begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into localVariable 2 end copies the codeCopy the code
In this bytecode sequence, the first two instructions iloAD_0 and ILoAD_1 push the integers indexed 0 and 1 stored in the local table into the operand stack, and the iADD instruction adds the two integers from the operand stack and pushes the result into the operand stack. The fourth instruction, istore_2, pops the result from the operand stack and stores it at the local variable table index 2.
The following figure details the state changes of the local variable table and operand stack during this process (areas of the local variable table and operand stack that are not used in the figure are represented by blank Spaces).
3). Dynamic linking
Each stack frame contains a reference to a method that belongs in the runtime constant pool and is held to support dynamic linking during method calls.
There are a lot of symbolic references in the constant pool of the Class file, and the method invocation instructions in the bytecode take symbolic references to methods in the constant pool as arguments. These symbols quote:
- Static resolution: part will be inClass loading phaseOr the first time you use itDirect reference(e.g.,
final
,static
Domain, etc.), calledStatic analysis. - Dynamic parsing: The other part is converted to a direct reference during each run, called dynamic linking.
4). Method returns the address
When a method starts executing, there are only two ways to exit the current method:
- Return to normalWhen an execution encounters a return instruction, it passes the return value to the upper method caller. This exit is calledNormal exit completion(
Normal Method Invocation Completion
), generally, the caller’sPC
counterCan be used as a return address. - Abnormal return: When an exception is encountered and the current method body is not handled, the method will exit, and there is no return value, calledAbnormal completion exit(
Abrupt Method Invocation Completion
), the return address to be passedException handlerTable to determine.
When a method returns, it may do three things in sequence:
- Restores the local variable table and operand stack of the upper method.
- The operand stack that pushes the return value into the caller’s stack frame.
- will
PC
counterThe value of the pointThe next articleMethod instruction position.
Summary:
Note: In the Java Virtual Machine specification, two exceptions are specified for this area. StackOverflowError is raised if the stack depth requested by the current thread is greater than the virtual machine stack allows (if the virtual machine stack does not allow dynamic scaling). Second, an OutOfMemoryError is thrown if sufficient memory cannot be allocated during expansion.
1.3. Local method stack
The role played by the Native method stack and the Java Virtual machine stack is very similar, with the main difference being that the Java virtual machine stack executes Java method services, while the Native method stack executes Native method services (usually written in C).
Some virtual machine distributions, such as the Sun HotSpot VIRTUAL Machine, directly merge the local method stack with the Java virtual machine stack. Like the virtual stack, the local method stack throws StackOverflowError and OutOfMemoryError exceptions.
1.4. The heap
The Java heap is the largest area of memory shared by all threads and is created when the virtual machine is started. The sole purpose of this memory area is to hold object instances, and almost all object instances are allocated memory here.
In Java, the heap is divided into two distinct regions: Young Generation and Old Generation. The New generation (Young) is divided into three zones: one Eden zone and two Survivor zones – From Survivor zone and To Survivor zone.
Brief summary: New object allocation is first placed in Eden area of the Young Generation, Survivor area serves as a buffer between Eden area and Old area. If the object in Survivor area survives several collections, it will be transferred to Old area.
The purpose of this partition is to enable the JVM to better manage objects in heap memory, including memory allocation and reclamation.
1.5. Methods area
The method area, like the Java heap, is shared by multiple threads and is used to store data such as class information, constants, static constants, and just-in-time compiled code.
1.6. Runtime constant pools
The runtime constant pool is part of the method area. In addition to the description of the Class version, fields, methods, and interfaces, a Class file contains a constant pool of information used to store various literal and symbolic references generated during compilation.
1.7. Direct memory
Direct memory is not part of the virtual machine run-time data area, nor is it a memory area defined in the Java Virtual Machine specification. Java NIO allows Java programs to access direct memory, which is usually faster than Java heap memory. Therefore, direct memory is recommended for scenarios with frequent read and write operations and high performance requirements.
(2). Common memory overflow exceptions
In addition to program counters, OutOfMemoryError can occur in any other runtime area of the Java virtual machine, as verified below:
2.1. Java heap overflow
The Java heap can store object instances. Avoid garbage collection by constantly creating objects and ensuring that GC Roots have a reachable path to them. An OutOfMemoryError is raised when the number of objects reaches the maximum heap capacity limit.
Set the JVM startup parameters: -xMS20m sets the minimum heap memory to 20M, and -xmx20m sets the maximum heap memory to be the same as the minimum. This prevents the Java heap from automatically expanding when it runs out of memory. – XX: + HeapDumpOnOutOfMemoryError parameters can make the virtual machine in the event of a memory exceptions when the snapshot Dump out the memory heap runtime.
HeapOOM.java
/**
* VM Args: -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
public static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) { list.add(new OOMObject()); }}} copy the codeCopy the code
Test results:
Open the Dump file for the Java VisualVM export Heap memory runtime.
HeapOOM
Objects are constantly created and heap memory usage is reached
99%
.
Garbage collectorRepeated attempts at recycling have failed.
Analysis: In this case, there are usually two possibilities to consider: memory leak and memory overflow.
- If it is memory leak:
Further analysis was performed using the Java VisualVM tool to see how the leak object was associated with GC Roots so that the garbage collector could not collect it.
- If it is memory overflow:
As analyzed by the Java VisualVM tool, there are no leaking objects, that is, objects in heap memory must survive. Consider the following measures:
- Check the code to see if there are some objects with too long life cycle and too long duration, and try to reduce the memory of the program runtime.
- Check the virtual machineHeap parameters(
-Xmx
with-Xms
), compared to the machinePhysical memoryLet’s see if we can turn it up.
2.2. Virtual machine and local method stack overflow
In terms of vm stacks and local method stacks, there are two possible memory exception types:
- If the scene is requestedThe stack depthThe value is greater than the value allowed by the VIRTUAL machineMaximum depthThat will be thrown
StackOverflowError
The exception. - If the VM is inExtend the stackCannot apply for enoughmemorySpace, might throw
OutOfMemoryError
The exception.
The problem can be divided into two types: when the stack space cannot be allocated, whether the stack memory is too small or the used stack memory is too large.
Abnormal StackOverflowError
Test Plan 1:
- use
-Xss
Parameters to reduceStack memoryThe capacity to print when an exception occursThe stackThe depth. - Define a large number of local variables to increase the length of the local variable table in the stack frame.
-xss128K Sets the stack memory size to 128K.
JavaVMStackSOF.java
/**
* VM Args: -Xss128k
*/
public class JavaVMStackSOF {
private int stackLength = 1;
private void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("Stack length: "+ oom.stackLength); throw e; }}} copy the codeCopy the code
Test results:
In a single thread, a StackOverflowError is raised when memory cannot be allocated, whether the stack frame is too large or the stack size is too small.
Test Plan 2:
- Keep creating threads and keeping them running.
JavaVMStackOOM.java
/**
* VM Args: -Xss2M
*/
public class JavaVMStackOOM {
private void running() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
new Thread(new Runnable() {
@Override
public void run() { running(); } }).start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); }} Copy the codeCopy the code
Test results:
Exception in thread "main"Java. Lang. OutOfMemoryError: unable to create new native thread to copy codeCopy the code
When the above test code runs, there is a large risk that may cause the operating system to suspend operation, so I will not test it myself here, citing the author’s test results.
2.3. Method area and runtime constant pool overflow
(I). Constant pool memory overflow test at runtime
Runtime constants and literals are stored in the runtime constant pool, which is part of the method area, so the tests for both areas are the same. Here we use string.Intern () to test:
String. Intern () is a native method that returns a String in the String constant pool if it exists. Otherwise, the String contained in the String is put into the constant pool and a reference to the String is returned.
Set JVM startup parameters: -xx :PermSize=10M and -xx :MaxPermSize=10M limit the size of the method area to 10M, indirectly limiting the capacity of the constant pool.
RuntimeConstantPoolOOM.java
/** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M */ public class RuntimeConstantPoolOOM { public static void main(String[] args) { List<String> List = new ArrayList<>(); // 10MB PermSize is enough to generate OOM in the Integer range.while (true) { list.add(String.valueOf(i++).intern()); }}} copy the codeCopy the code
Analysis of test results:
JDK1.6 running result:
Exception in thread "main"Java. Lang. OutOfMemoryError: PermGen space at Java lang. String. The intern (Native Method) to copy codeCopy the code
The JDK1.6 runtime results show that the constant pool overflows and throws outofMemoryErrors with permanent strings. JDK1.7 and above will not get the same results, and it will loop on and on.
(2) method area memory overflow test
The method area holds class-related information, such as Class names, access modifiers, constant pools, field descriptions, method descriptions, and so on. For method area memory overflow testing, the basic idea is to generate a large number of byte-like areas to fill the method area at run time.
The bytecode technology of Spring framework’s CGLib dynamic proxy is introduced here, and new proxy classes are generated continuously through the loop to achieve the effect of overflow of method area memory.
JavaMethodAreaOOM.java
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
returnproxy.invokeSuper(obj, args); }}); enhancer.create(); } } private static class OOMObject { publicOOMObject() {}}} copy codeCopy the code
JDK1.6 running result:
Exception in thread "main"java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616) Copy the codeCopy the code
Analysis of test results:
The JDK1.6 runtime results show that the constant pool overflows and throws outofMemoryErrors with permanent strings. JDK1.7 and above will not get the same results, and it will loop on and on.
2.4. Direct memory overflow
The size of native direct memory can be specified with -xx :MaxDirectMemorySize, or if not specified, defaults to the maximum Java heap size (specified by -xmx).
Test scenario:
Retrieving an Unsafe instance directly uses reflection to request memory allocation from the operating system:
Set the JVM startup parameters: -xmx20m specifies the maximum memory of the Java heap, and -xx :MaxDirectMemorySize=10M specifies the size of direct memory.
DirectMemoryOOM.java
/**
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) { unsafe.allocateMemory(_1MB); }}} copy the codeCopy the code
Test results:
Analysis of test results:
An obvious feature of a memory overflow caused by DirectMemory is that no obvious exception information is seen in the Heap Dump file. If the Dump file is small after OOM and NIO is used directly or indirectly, consider this.