This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

Java memory area and memory overflow exception

Runtime data area

Program counter

  • Used to record the address of the next instruction to be executed from memory, a small area of memory that is private to the thread and the only area that does not raise an OOM exception

Java virtual machine stack

  • The Java Virtual Machine Stack is thread-private and has the same life cycle as a thread. The virtual machine Stack describes the threaded memory model of Java method execution: For each method execution, 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

  • StackOverflowError is thrown if the stack depth requested by the thread is greater than the depth allowed by the virtual machine

  • If the Java virtual machine stack can expand dynamically, OutOfMemoryError will be raised when sufficient memory cannot be allocated during stack expansion

Local method stack

  • The local method stack serves the local methods used by the virtual machine, and the Java virtual machine stack serves the Java methods (bytecode) executed by the virtual machine

The Java heap

  • For Java applications, the Java Heap is the largest chunk 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. 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
  • An OOM exception is raised when there is not enough heap memory to allocate to an object instance and the heap memory cannot be expanded

Methods area

  • The method area, similar to the Java heap, is shared by threads and is used to store type information that has been loaded by the virtual machine, constants, static variables, just-in-time compiled code caches, and so on
  • The alias “non-heap” is often used to distinguish it from the Java heap
  • If the method area does not have enough space to allocate memory, an OOM exception will be raised

Run-time constant pool

  • The runtime constant pool is the part of the method area that holds the various literal and symbolic references generated at compile time
  • An OOM exception is raised when the constant pool cannot allocate memory due to the method area memory limit

Direct memory

  • Direct memory is not part of the runtime data area, but it is limited by total memory and may cause OOM exceptions

HotSpot VIRTUAL machine object Exploration

Object creation

After the class load check passes, the virtual machine allocates memory for the new object in one of two main ways:

  • Pointer to the collision

  • The free list

Object memory layout

In the HotSpot virtual machine, the object storage layout in the heap memory can be divided into three parts: object Header, Instance Data, and align Padding.

  • Object head

    • Store the object’s own runtime data (Mark Word), such as HashCode, GC generation age, lock status flags, locks held by threads, bias thread IDS, bias time stamps, etc
    • Type pointer (a pointer to an object’s type metadata)
  • The instance data

    • Object actually stores valid information, that is, the contents of each type of field in your code
  • Alignment filling

    • Since the Automatic memory management system of the HotSpot VIRTUAL machine requires that the object start address be an integer multiple of 8 bytes, meaning that any object size is an integer multiple of 8 bytes, alignment padding is needed to act as placeholder completion if the instance data part is not aligned

Object access location

Java programs use reference data on the stack to manipulate specific objects on the heap, which is accessed by the virtual machine.

There are two main access methods:

  • handle

  • Direct Pointers

Actual OOM Exception

Using different JDK and garbage collector can produce different results. The following examples use JDK8 and ParallelGC garbage collector to run the code

The default garbage collector VM parameter -xx :+PrintCommandLineFlags -versionCopy the code

The Java heap overflow

As long as you keep creating object instances while avoiding garbage collection, reaching the maximum heap capacity will generate OOM exceptions

public class Hello {
    / * * * - Xms: minimum heap memory 20 m -xmx: maximum heap memory out of 20 m both set automatic extension * VM arguments: - Xms20M - Xmx20M - XX: + HeapDumpOnOutOfMemoryError * /
    public static void main(String[] args) {
        List<Hello> hellos = new ArrayList<>();
        while (true) {
            hellos.add(newHello()); }}}Copy the code

Java virtual machine stack and local method stack overflow

The Java Virtual Machine Specification explicitly allows the Java VIRTUAL machine to choose whether to support dynamic stack extension or not, whereas the HotSpot virtual machine does not support extension, so unless an OutOfMemoryError occurs when the thread is created and cannot obtain enough memory, Otherwise, there will be no memory overflow due to expansion while the thread is running, only StackOverflowError due to stack size not being able to accommodate the new stack frame

  • Reduce stack size with the -xss argument
public class Hello {
    /**
     * VM参数:-Xss128k
     */
    private int stackLength = 1;
    public void stackLeak(a) {
        stackLength++;
        // Call the method recursively
        stackLeak();
    }
    public static void main(String[] args) throws Throwable {
        Hello oom = new Hello();
        try {
            // call the method and push it
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throwe; }}}Copy the code

  • A number of local variables are defined to increase the length of the local variable table in this method frame (i.e. resize the stack frame)
public class Hello {
    private static int stackLength = 0;

    public static void test(a) {
        // The number of local variables increases, and the stack frame increases
        long unused1, unused2, unused3, unused4, unused5,
                unused6, unused7, unused8, unused9, unused10,
                unused11, unused12, unused13, unused14, unused15,
                unused16, unused17, unused18, unused19, unused20,
                unused21, unused22, unused23, unused24, unused25,
                unused26, unused27, unused28, unused29, unused30,
                unused31, unused32, unused33, unused34, unused35,
                unused36, unused37, unused38, unused39, unused40,
                unused41, unused42, unused43, unused44, unused45,
                unused46, unused47, unused48, unused49, unused50,
                unused51, unused52, unused53, unused54, unused55,
                unused56, unused57, unused58, unused59, unused60,
                unused61, unused62, unused63, unused64, unused65,
                unused66, unused67, unused68, unused69, unused70,
                unused71, unused72, unused73, unused74, unused75,
                unused76, unused77, unused78, unused79, unused80,
                unused81, unused82, unused83, unused84, unused85,
                unused86, unused87, unused88, unused89, unused90,
                unused91, unused92, unused93, unused94, unused95,
                unused96, unused97, unused98, unused99, unused100;
        stackLength++;
        // call recursivelytest(); unused1 = unused2 = unused3 = unused4 = unused5 = unused6 = unused7 = unused8 = unused9 = unused10 = unused11 = unused12  = unused13 = unused14 = unused15 = unused16 = unused17 = unused18 = unused19 = unused20 = unused21 = unused22 = unused23 = unused24 = unused25 = unused26 = unused27 = unused28 = unused29 = unused30 = unused31 = unused32 = unused33 =  unused34 = unused35 = unused36 = unused37 = unused38 = unused39 = unused40 = unused41 = unused42 = unused43 = unused44 = unused45 = unused46 = unused47 = unused48 = unused49 = unused50 = unused51 = unused52 = unused53 = unused54 = unused55  = unused56 = unused57 = unused58 = unused59 = unused60 = unused61 = unused62 = unused63 = unused64 = unused65 = unused66 = unused67 = unused68 = unused69 = unused70 = unused71 = unused72 = unused73 = unused74 = unused75 = unused76 =  unused77 = unused78 = unused79 = unused80 = unused81 = unused82 = unused83 = unused84 = unused85 = unused86 = unused87 = unused88 = unused89 = unused90 = unused91 = unused92 = unused93 = unused94 = unused95 = unused96 = unused97 = unused98  = unused99 = unused100 =0;
    }

    public static void main(String[] args) {
        try {
            test();
        } catch (Error e) {
            System.out.println("stack length:" + stackLength);
            throwe; }}}Copy the code

Method area and runtime constant pool overflow

  • Method Area capacity control
public class Hello {
    -xx :MetaspaceSize=6M -xx :MaxMetaspaceSize=6M */ ** * JDK8VM parameters: -xx :PermSize=6M -xx :MaxMetaspaceSize=6M */
    public static void main(String[] args) {
        // Use Set to keep constant pool references to avoid Full GC reclaiming constant pool behavior
        Set<String> set = new HashSet<>();
        // Short is enough to generate OOM for the 6M PermSize (permanent generation, JDK8 pre-metacreage and later)
        short i = 0;
        // Before JDK8, an OOM exception is thrown
        // in JDK8, it normally goes into an infinite loop and does not throw any exceptions
        while (true) {
            // string.intern () enters the String constant poolset.add(String.valueOf(i++).intern()); }}}Copy the code

The above code does not throw any exceptions in the JDK8 environment because the string constant pool has been moved to the Java heap and the size of the control method area has no effect on the Java heap

  • String.intern()If the String constant pool already contains a String equal to this String, return the String from the constant pool. Otherwise, the String contains a copy of the characters added to the constant pool and a reference to the String is returned
/** * JDK6: false false */
public static void main(String[] args) {
    String str1 = new StringBuilder("Computer").append("Software").toString();
    System.out.println(str1.intern() == str1);
    String str2 = new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern() == str2);
}
Copy the code
  • JDK6 because new StringBuilder() allocates Java heap memory and String.intern() copies the first encountered String into the String constant pool (method area), both are false

  • JDK8 because the string constant pool is moved to the Java heap, new StringBuilder() is allocated to the Java heap, and the string constant pool records the first instance reference encountered. So string.Intern () and new StringBuilder() are the same (true); Since the Java String was already in the constant pool when the sun.misc.version class was loaded, the intern() method returns the String from the current constant pool. New StringBuilder() creates a new one in the heap.

  • The main responsibility of the method area is to store type-related information, such as class names, access modifiers, constant pools, field descriptions, method descriptions, etc., so a large number of classes filling the method area at run time can also cause method area overflow

-xx :MetaspaceSize=10M -XX:MaxMetaspaceSize=10M */
public class Hello {
    public static void main(String[] args) {
        while (true) {
           // Create a CgLib enhanced object
            Enhancer enhancer = new Enhancer();
            // Sets the proxied class
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            // Specify the interceptor
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    returnproxy.invokeSuper(obj, args); }});// Create a proxy objectenhancer.create(); }}static class OOMObject {}}Copy the code

Native direct memory overflow

The size of DirectMemory can be specified with the -xx: MaxDirectMemorySize parameter. If not specified, it defaults to the maximum Java heap size (specified by -xmx)

// Use unsafe to allocate native memory
public class Hello {
    // VM parameters: -xmx20m-xx :MaxDirectMemorySize=10M
    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) {
            // The memory is actually allocatedunsafe.allocateMemory(_1MB); }}}Copy the code

The resources

Chapter 2 in Understanding the Java Virtual Machine (3rd edition) : Java memory regions and memory overflow exceptions