JVM memory structure

JVM introduction

1. What is a JVM?

java virtual machine - the running environment of java programs (java Binary Bytecode operating environment)

2. Benefits of JVM

  1. The cornerstone of writing once and running everywhere [key]
  2. Automatic memory management, garbage collection function [key]
  3. Data subscript out of range check
  4. Polymorphism, object-oriented programming

3. Comparison of JVM, JRE and JDK

4. What is the use of learning JVM?

  • interview
  • Understand the underlying implementation principle
  • Necessary skills of middle and senior programmers

5. What is the composition of the JVM?

Memory structure of JVM

JVM class loading process

6. Common JVM

JVM memory structure

  1. Program counter
  2. VM Stack
  3. Native Method Stack
  4. heap
  5. Method area (meta space)

1. Program counter PC Register

1. Definition

Program Counter Register

2. Function

  • Function: remember the execution address of the next jvm instruction
  • Features: thread private; There is no memory overflow, and it is the only area in the JVM specification that does not have OutOfMemoryError
  • Binary bytecode: JVM instruction - > interpreter - > machine code - > CPU
  • Program counter: remember the execution address of the next jvm instruction, and the hardware is realized through [Register]

Example: binary bytecode: JVM instruction java source code

2. Virtual machine stack JVM Stacks


Let's first understand the data structure of the stack

  • Stack stack, first in and last out FILO
  • Stack - memory space required for thread operation
  • Stack frame - memory required for each method to run
  • Stack - composed of multiple stack frames

1. Definition

Java virtual machine stacks

  1. The memory required by each thread to run is called virtual machine stack
  2. Each stack is composed of multiple stack frames, corresponding to the memory occupied by each method call
  3. Each thread can only have one active stack frame, corresponding to the method currently executing

2. Stack problem

1. Does garbage collection involve stack memory?

Stack memory does not involve garbage collection

2. The larger the stack memory allocation, the better?

The larger the stack memory is, the better. If it is set too large, it will affect the number of available threads; For example, -Xss1m, -Xss2m, with the total memory unchanged, the number of available threads will be reduced

3. Is the local variable in the method thread safe?

The local variables in the method are thread safe (without escaping from the scope of the method), because the local variables in the method are in their own independent memory; If static int is shared by threads, it is not thread safe; It mainly depends on whether the variables are shared by threads or private by threads
be careful:

  • If a local variable in a method does not escape the scope of the method, it is thread safe
  • If a local variable references an object and escapes the scope of the method, thread safety needs to be considered

3. Stack memory overflow

java.lang.StackOverflowError
Note: you can use -Xss to adjust the size of stack memory, for example: -Xss256k
Too many stack frames lead to stack memory overflow. For example, recursion does not set an end condition. Our production environment recommends not to use recursion as much as possible
The stack frame is too large, which leads to stack memory overflow. For example, the method is written too long, and there are too many local variables, which leads to the stack frame bursting the stack memory. This situation is relatively rare

4. Thread running diagnosis

1. How to diagnose if the CPU occupation is too high

  1. Use top to locate which process takes up too much cpu

  1. ps H -eo pid,tid,%cpu check all processes, threads and CPU consumption of linux

  1. ps H -eo pid,tid,%cpu | grep process id use the ps command to further locate which thread caused the CPU occupation to be too high

  1. Jstack process pid checks the thread information. The thread id(nid) output by jstack is hexadecimal, and the decimal thread id needs to be converted to hexadecimal; You can find the problematic thread according to the hexadecimal thread id and further locate the source code line number of the problem code


Through the above methods, we found the file and line number with high CPU consumption in the source code

2. How to diagnose if the program runs for a long time without results

nohup java -cp /root/JvmLearn-1.0-SNAPSHOT.jar com.jvm.stack.T07_StackDeadLock &
Run a java program for a long time without results. Use jstack thread id to check whether a deadlock has occurred

3. Native Method Stacks

The local method stack is not the code written in Java, but in C/C++, which calls the bottom layer through C/C++

java calls C or c++ code to call the underlying

4. Heap heap

1. Definition

  • Through the new keyword, heap memory will be used when creating objects
  • characteristic
    • It is shared by threads, and the objects in the heap need to consider thread safety
    • There is a garbage collection mechanism

2. Heap memory overflow

java.lang.OutOfMemoryError
Code reference: cn.itcast.jvm.t1.heap.demo1_ five

/**
 * Demonstrate heap memory overflow java.lang.OutOfMemoryError: Java heap space
 * -Xmx8m
 */
 
public class T01_HeapOutOfMemoryError {
 
    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "hello";
            while (true) {
                list.add(a); // hello, hellohello, hellohellohellohello ...
                a = a + a;  // hellohellohellohello
                i++;
                TimeUnit.MILLISECONDS.sleep(2000);
            }
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(i);
        }
    }
 
}

Production suggestions

Suggestions for production environment: if the memory is large, the memory overflow will not be exposed so quickly; At this time, we can reduce the heap memory to expose the memory overflow as soon as possible
-Xmx example of setting the maximum value of heap memory space: -Xmx8m
-Example of initial value of Xms setting to memory space: -Xms8m

3. Heap memory diagnosis

  • jps tool: check which java processes are in the current system
  • Jmap tool: check the heap memory usage jmap -heap PID (process id) (you can only query the heap memory usage at a certain time)
  • jstack tool: thread monitoring
  • jconsole tool: graphical interface, multi-functional detection tool, which can continuously monitor
  • jvisualvm tool: graphical interface, multi-functional detection tool, which can continuously monitor; And dump

Code reference: cn.itcast.jvm.t1.heap.demo1_ four

public class Demo1_4 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("1...");
        Thread.sleep(30000);
        byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb
        System.out.println("2...");
        Thread.sleep(20000);
        array = null;
        System.gc();
        System.out.println("3...");
        Thread.sleep(1000000L);
    }
}
  1. Run the program, and jps finds the process id of the program
E:\BaiduNetDisk\Download\data--jvm dark horse\code\jvm>jps
63904 RemoteMavenServer36
1348
48964 HbteSaasQmsApplication
51844 HbteSaasPurchaseApplication
51112 HbteSaasGatewayApplication
62728 Launcher
40812 Launcher
53228 HbteSaasProduceApplication
47504 Jps
54096 HbteSaasStoreApplication
57104 SharpApplication
48308 HbteSaasPlatformApplication
48628 HbteSaasAuthApplication
49204 Launcher
65172 Demo1_4
62908 Launcher
  1. Use jmap -heap PID (process id) to check the changes of heap memory at the time of printing 1, 2 and 3 respectively
E:\BaiduNetDisk\Download\data--jvm dark horse\code\jvm>jmap -heap 65172
Attaching to process ID 65172, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4246732800 (4050.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1415577600 (1350.0MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 66584576 (63.5MB)
   used     = 5327016 (5.080238342285156MB)
   free     = 61257560 (58.419761657714844MB)
   8.000375342181348% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 177733632 (169.5MB)
   used     = 0 (0.0MB)
   free     = 177733632 (169.5MB)
   0.0% used

1736 interned Strings occupying 177872 bytes.

E:\BaiduNetDisk\Download\data--jvm dark horse\code\jvm>jmap -heap 65172
Attaching to process ID 65172, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4246732800 (4050.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1415577600 (1350.0MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 66584576 (63.5MB)
   used     = 15812792 (15.080253601074219MB)
   free     = 50771784 (48.41974639892578MB)
   23.748430867833417% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 177733632 (169.5MB)
   used     = 0 (0.0MB)
   free     = 177733632 (169.5MB)
   0.0% used

1737 interned Strings occupying 177920 bytes.

E:\BaiduNetDisk\Download\data--jvm dark horse\code\jvm>jmap -heap 65172
Attaching to process ID 65172, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4246732800 (4050.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1415577600 (1350.0MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 66584576 (63.5MB)
   used     = 2663424 (2.5400390625MB)
   free     = 63921152 (60.9599609375MB)
   4.0000615157480315% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 177733632 (169.5MB)
   used     = 766440 (0.7309341430664062MB)
   free     = 176967192 (168.7690658569336MB)
   0.43122958293003316% used

1723 interned Strings occupying 176928 bytes.
  1. After the program runs, use jconsole on the console to connect the monitored processes (monitor the processes between jsonsole s)






1. After garbage collection, the memory occupation is still very high, and the troubleshooting method

Code reference: cn.itcast.jvm.t1.heap.demo1_ thirteen

/**
 * Show me how to view the number of objects heap dump
 */
public class Demo1_13 {

    public static void main(String[] args) throws InterruptedException {
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 200; i++) {
            students.add(new Student());
//            Student student = new Student();
        }
        Thread.sleep(1000000000L);
    }
}
class Student {
    private byte[] big = new byte[1024*1024];
}

Solution:
Jvisualvm can use dump to find the largest object heap dump dump (based on the above problems, use tools to check); In the test environment, we can open the dump file record, and then import the dump file into the jvisualvm tool to see which objects occupy the most memory.



Through heap dump, download the dump file and analyze the dump file with jvisualvm, view large objects, find problems, and troubleshoot the original code
reference resources:
Get Dump file of java application under Linux
Stack dump file of export project in linux Environment

5. Method Area (Metaspace)

1. Definition

  • Thread sharing
  • It is created when the JVM is started and logically belongs to a part of the heap (see the manufacturer's implementation)
  • Method area may also overflow memory

2. Composition

3. Method area memory overflow

Before 1.8, it will cause permanent generation memory overflow

  • Demonstrate permanent generation memory overflow: java.lang.OutOfMemoryError: PerGen space
  • -XX:MaxPerSize=8m

After 1.8, it will cause meta space memory overflow (system memory)

  • Demonstrate Metaspace memory overflow java.lang.OutOfMemoryError: Metaspace
  • -XX:MaxMetaspaceSize=8m

1. Demonstration case of meta space memory overflow

I set the size of the meta space to be very small, and there are many dynamically loaded classes, resulting in the meta space OOM
Jdk1.8 reference code: cn.itcast.jvm.t1.metaspace.demo1_ eight

Test on the computer by yourself. The problem of meta space overflow will occur only when it is adjusted to about 20M, and the number of cycles should be increased

/**
 * Demonstrate Metaspace memory overflow java.lang.OutOfMemoryError: Metaspace
 * -XX:MaxMetaspaceSize=20m
 */
public class Demo1_8 extends ClassLoader { // Binary bytecode that can be used to load classes
    public static void main(String[] args) {
        int j = 0;
        try {
            Demo1_8 test = new Demo1_8();
            for (int i = 0; i < 10000000; i++, j++) {
                // ClassWriter is used to generate binary bytecode of classes
                ClassWriter cw = new ClassWriter(0);
                // Version number, public, class name, package name, parent class, interface
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                // Return byte[]
                byte[] code = cw.toByteArray();
                // Class loading performed
                test.defineClass("Class" + i, code, 0, code.length); // Class object
            }
        } finally {
            System.out.println(j);
        }
    }
}

If you set the size of the meta space to 8m, you will be prompted that the space is too small


If it is set to 12m, OOM will be reported, prompting to compress the class space


2. Meta space memory overflow occurs in the production environment, and these aspects should be locked

Although the program we write doesn't use a lot of dynamically loaded classes, if we use some external frameworks, we may load a lot of dynamically loaded classes, which may lead to meta space memory overflow.
Scenario (dynamically loading classes), if the framework is used improperly, it will also lead to memory overflow in the method area

  • spring
  • mybatis

4. Runtime constant pool

  • The constant pool is a table from which the virtual machine instruction finds the class name, method name, parameter type, literal and other information to be executed
  • Runtime constant pool, constant pool is * In the class file, when the class is loaded, its constant pool information will be put into the runtime constant pool, and the symbolic address inside will be changed into the real address (symbolic reference will be changed into direct reference)

In short, after the class is compiled, the constant pool is in the class bytecode file. The virtual machine instruction finds the constant information (class name, method name, parameter type, literal) it wants to use through the symbolic address. When the class file is loaded by the jvm virtual machine, it will create the class template object and allocate memory for it, At this time, the instructions of the virtual machine in the calss file need to find the constant information in the constant pool in the memory. The symbolic address is converted into the real memory address, and the constant pool information loaded into the memory is directly found through the memory address

1. Principle demonstration of string constant pool JVM bytecode

Code example: cn.itcast.jvm.t5.HelloWorld

// Binary bytecode (class basic information, constant pool, class method definition (including virtual machine instructions))
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

Disassemble the above compiled class file: javap -v helloword Class decompiles as follows:

E:\Learning documents\jvm>cd E:\Learning documents\jvm\out\production\jvm\cn\itcast\jvm\t5

E:\Learning documents\jvm\out\production\jvm\cn\itcast\jvm\t5>javap -v HelloWorld.class
Classfile /E:/Learning documents/jvm/out/production/jvm/cn/itcast/jvm/t5/HelloWorld.class
  Last modified 2022-7-6; size 567 bytes
  MD5 checksum 8efebdac91aa496515fa1c161184e354
  Compiled from "HelloWorld.java"
public class cn.itcast.jvm.t5.HelloWorld
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // hello world
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // cn/itcast/jvm/t5/HelloWorld
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcn/itcast/jvm/t5/HelloWorld;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               HelloWorld.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               hello world
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               cn/itcast/jvm/t5/HelloWorld
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public cn.itcast.jvm.t5.HelloWorld();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/itcast/jvm/t5/HelloWorld;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"

5. Stringtable (string pool - string constant pool)

StringTable interview questions

String s1 = "a";
String s2 = "b";
String s3 = "a"+"b"; //ab
String s4 = s1+s2; //new String("ab")
String s5 = "ab";
String s6 = s4.intern();
//ask
System.out.println(s3==s4); //false
System.out.println(s3==s5); //true
System.out.println(s3==s6); //true

String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();

//Ask, what if the location of [the last two lines of code] is changed, and what if it is jdk1.6
System.out.println(x1==x2);

1. Relationship between stringtable constant pool and string pool

Code reference: cn.itcast.jvm.t1.stringtable.demo1_ twenty-two

// Stringtable ["a", "B", "ab"] hashtable structure, cannot be expanded
public class Demo1_22 {
    // The information in the constant pool will be loaded into the runtime constant pool, 
    // At this time, a, B and ab are symbols in the constant pool, and have not yet become java string objects
    // ldc #2 will change the a symbol into a "a" string object, and the string will not be loaded into the runtime constant pool until the corresponding code is executed
    // ldc #3 will change the b symbol into a "b" string object
    // ldc #4 will change AB symbol into "ab" string object

    public static void main(String[] args) {
        String s1 = "a"; // Lazy
        String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2; // new StringBuilder().append("a").append("b").toString()  new String("ab")
        String s5 = "a" + "b";  // The optimization of javac during compilation has been determined as ab during compilation

        System.out.println(s3 == s4);//Print false
        System.out.println(s3 == s5);//Print true
    }
}

javap -v Demo1_22.class decompiles as follows:

E:\Learning documents\jvm\out\production\jvm\cn\itcast\jvm\t1\stringtable>javap -v Demo1_22.class
Classfile /E:/Learning documents/jvm/out/production/jvm/cn/itcast/jvm/t1/stringtable/Demo1_22.class
  Last modified 2022-7-6; size 1039 bytes
  MD5 checksum 8815b8f391f7f5387dc065e4053bf5d1
  Compiled from "Demo1_22.java"
public class cn.itcast.jvm.t1.stringtable.Demo1_22
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#36        // java/lang/Object."<init>":()V
   #2 = String             #37            // a
   #3 = String             #38            // b
   #4 = String             #39            // ab
   #5 = Class              #40            // java/lang/StringBuilder
   #6 = Methodref          #5.#36         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#41         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#42         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Fieldref           #43.#44        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #45.#46        // java/io/PrintStream.println:(Z)V
  #11 = Class              #47            // cn/itcast/jvm/t1/stringtable/Demo1_22
  #12 = Class              #48            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               LocalVariableTable
  #18 = Utf8               this
  #19 = Utf8               Lcn/itcast/jvm/t1/stringtable/Demo1_22;
  #20 = Utf8               main
  #21 = Utf8               ([Ljava/lang/String;)V
  #22 = Utf8               args
  #23 = Utf8               [Ljava/lang/String;
  #24 = Utf8               s1
  #25 = Utf8               Ljava/lang/String;
  #26 = Utf8               s2
  #27 = Utf8               s3
  #28 = Utf8               s4
  #29 = Utf8               s5
  #30 = Utf8               StackMapTable
  #31 = Class              #23            // "[Ljava/lang/String;"
  #32 = Class              #49            // java/lang/String
  #33 = Class              #50            // java/io/PrintStream
  #34 = Utf8               SourceFile
  #35 = Utf8               Demo1_22.java
  #36 = NameAndType        #13:#14        // "<init>":()V
  #37 = Utf8               a
  #38 = Utf8               b
  #39 = Utf8               ab
  #40 = Utf8               java/lang/StringBuilder
  #41 = NameAndType        #51:#52        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #42 = NameAndType        #53:#54        // toString:()Ljava/lang/String;
  #43 = Class              #55            // java/lang/System
  #44 = NameAndType        #56:#57        // out:Ljava/io/PrintStream;
  #45 = Class              #50            // java/io/PrintStream
  #46 = NameAndType        #58:#59        // println:(Z)V
  #47 = Utf8               cn/itcast/jvm/t1/stringtable/Demo1_22
  #48 = Utf8               java/lang/Object
  #49 = Utf8               java/lang/String
  #50 = Utf8               java/io/PrintStream
  #51 = Utf8               append
  #52 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = Utf8               toString
  #54 = Utf8               ()Ljava/lang/String;
  #55 = Utf8               java/lang/System
  #56 = Utf8               out
  #57 = Utf8               Ljava/io/PrintStream;
  #58 = Utf8               println
  #59 = Utf8               (Z)V
{
  public cn.itcast.jvm.t1.stringtable.Demo1_22();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/itcast/jvm/t1/stringtable/Demo1_22;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=6, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: ldc           #4                  // String ab
        31: astore        5
        33: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        36: aload_3
        37: aload         4
        39: if_acmpne     46
        42: iconst_1
        43: goto          47
        46: iconst_0
        47: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        50: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        53: aload_3
        54: aload         5
        56: if_acmpne     63
        59: iconst_1
        60: goto          64
        63: iconst_0
        64: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        67: return
      LineNumberTable:
        line 11: 0
        line 12: 3
        line 13: 6
        line 14: 9
        line 15: 29
        line 17: 33
        line 18: 50
        line 19: 67
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      68     0  args   [Ljava/lang/String;
            3      65     1    s1   Ljava/lang/String;
            6      62     2    s2   Ljava/lang/String;
            9      59     3    s3   Ljava/lang/String;
           29      39     4    s4   Ljava/lang/String;
           33      35     5    s5   Ljava/lang/String;
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 46
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 79 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "Demo1_22.java"


When you execute the corresponding virtual machine instruction and need to load the corresponding string in the constant pool of the class file into the runtime constant pool in memory, you will go to the StringTable to find out whether the string to be loaded is in the StringTable. If not, put it in, if yes, no longer put it in, and use the existing string constant

2. StringTable string delayed loading

Demonstrate that string literals are also deferred objects

/**
* The demonstration string literal is also the object of [delay]
*/
public class TestString {
    public static void main(String[] args) {
        int x = args.length;
        System.out.println(); // Number of strings 2238
        
        System.out.print("1");
        System.out.print("2");
        System.out.print("3");
        System.out.print("4");
        System.out.print("5");
        System.out.print("6");
        System.out.print("7");
        System.out.print("8");
        System.out.print("9");
        System.out.print("0");
        System.out.print("1"); // Number of strings 2248
        System.out.print("2");
        System.out.print("3");
        System.out.print("4");
        System.out.print("5");
        System.out.print("6");
        System.out.print("7");
        System.out.print("8");
        System.out.print("9");
        System.out.print("0");
        System.out.print(x); // Number of strings 2248
    }
}

The number of strings debugged here is 2238

After running here, 10 strings are loaded into the string pool StringTable, and the number of strings is 2248

After running here, the string pool StringTable has loaded the just 10 strings, so it does not continue to load. The number of strings is still 2248, which also proves that strings are loaded into the string pool late

6. StringTable feature

  • The string in the constant pool is only a symbol and becomes an object when it is first used
  • The string pool mechanism is used to avoid repeated creation of string objects
  • The principle of string variable splicing is StringBuilder (JDK1.8)
  • The principle of string constant splicing is compile time optimization
  • You can use the intern method to actively put string objects that are not in the string pool into the string pool
    • 1.8 try to put this string object into the string pool. If there is one, it will not be put in. If there is no one, it will be put into the string pool, and the object in the string pool will be returned
    • 1.6 try to put this string object into the string pool. If there is one, it will not be put in. If there is no one, it will copy the object and put it into the string pool, and the object in the string pool will be returned

StringTable_intern_1.8

Code reference: cn.itcast.jvm.t1.stringtable.demo1_ twenty-three

public class Demo1_23 {

    //  ["ab", "a", "b"]
    public static void main(String[] args) {

        String x = "ab";
        String s = new String("a") + new String("b");

        // Heap new string ("a") new string ("B") new string ("ab")
        String s2 = s.intern(); // Try to put this string object into the string pool. If there is one, it will not be put. If there is no one, it will be put into the string pool, and the object in the string pool will be returned

        System.out.println( s2 == x);
        System.out.println( s == x );
    }

}

7. StringTable position

  • In JDK1.6, the string constant pool is in the permanent generation;
  • JVM s of JDK1.7 and later versions have moved the runtime constant pool out of the method area and opened up an area in the Java Heap to store the runtime constant pool.
  • Starting with JDK1.8, the Java method area is cancelled and replaced by the metaSpace located in direct memory.

Comparison between JDK1.6 and JDK1.8 string constant pool

1. JDK1.8 string constant pool instance verification in heap

Code reference: cn.itcast.jvm.t1.stringtable.demo1_ six

/**
 * Demonstrate StringTable location
 * Set -Xmx10m -XX:-UseGCOverheadLimit under jdk8
 * Set -XX:MaxPermSize=10m under jdk6
 */
public class Demo1_6 {

    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        int i = 0;
        try {
            for (int j = 0; j < 260000; j++) {
                list.add(String.valueOf(j).intern());
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

Set the startup parameters of the JVM

-Xmx10m set the maximum size of heap memory to 10m
-20: -usegcoverheadlimit does not limit the running time of GC
reference resources:
JVM -XX: parameter introduction
JVM optimization -Xss -Xms -Xmx -Xmn parameter settings

Only when the size of heap memory is set to 10m, view the running results



Only 10M heap memory space is set, and oom appears, but the prompt message is that it exceeds the overhead limit of GC

The official JVM document describes this parameter, -XX:+UseGCOverheadLimit turns on the overhead limit of GC. If the memory reclaimed in 98% of the time is less than 2% of the memory, the JVM will be tasked and hopeless, indicating that the overhead limit of GC is exceeded

In order to see the hint of heap overflow, we turn off the overhead limit of GC



After turning off the overhead limit of GC, you can see that the heap memory shift overflows

8. StringTable garbage collection

Because in jdk1.8, the string constant pool is placed in the heap. If the heap space is insufficient, the string constant pool will also be garbage collected
Code reference: cn.itcast.jvm.t1.stringtable.demo1_ seven

/**
 * Demonstrate StringTable garbage collection
 * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
 */
public class Demo1_7 {
    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        try {
            for (int j = 0; j < 100000; j++) { // j=100, j=10000
                String.valueOf(j).intern();
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }

    }
}

JVM parameter settings

First cycle 100 times and let 100 strings into the pool


There are 1875 strings out of 100 that the program traverses into the pool, and other string constants used during program operation, such as class name, method name and so on, also exist in the string constant pool. Due to less traversal times, there are not many strings into the pool, and garbage collection is not triggered

Now go through 10000 times and put 10000 strings into the pool


After 10000 strings are put into the pool, 10M heap memory is insufficient, triggering garbage collection. Strings not used in the string constant pool are garbage collected, so there are only 8540 string constants in the string constant pool

9. StringTable performance tuning (case)

Code reference: cn.itcast.jvm.t1.stringtable.demo1_ twenty-four

  • Adjust -XX:StringTableSize= number of barrels
  • Consider whether to pool string objects

1. Use the -XX:StringTableSize= size parameter to increase the number of buckets to increase the performance of StringTable

/**
 * Demonstrate the impact of string pool size on Performance
 * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
 * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=10009
 *
 * The default bucket array size of string constant pool is 60013. The tuning of string constant pool is mainly to adjust the bucket data size;
 * If there are many strings, you need to increase the number of buckets to reduce the query complexity (hash collision probability)
 * hash If there are fewer collisions, the data in each hash bucket will not be too much and will be evenly distributed
 */
public class Demo1_24 {

    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern();
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
        }
    }
}

Reduce the StringTable bucket. The example operation is as follows:


Increase the StringTable bucket. The example operation is as follows:



Conclusion: it takes less time to put the same number of strings and increase the bucket of StringTable

2. Use the string constant pool to reduce memory usage in scenarios with more strings

/**
 * Demonstrate intern to reduce memory usage
 * -XX:StringTableSize=200000 -XX:+PrintStringTableStatistics
 * -Xsx500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000
 */
public class Demo1_25 {

    public static void main(String[] args) throws IOException {

        List<String> address = new ArrayList<>();
        System.in.read();
        for (int i = 0; i < 10; i++) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if(line == null) {
                        break;
                    }
                    address.add(line.intern());
                }
                System.out.println("cost:" +(System.nanoTime()-start)/1000000);
            }
        }
        System.in.read();
    }
}

Explain: jvm/linux Words is about 479829 words. The screenshot of the above code is as follows:

Read about 480000 wordsHeap memory footprinttime consuming
Not put into string poolAbout 300 megabytesShorter
Put into string poolAbout 100MBlonger

Operation result 1: not put into the string constant pool, screenshot of operation

address.add(line);


D:\IDEAWorkSpace\jvm_study\jvm\linux.words

Operation result 2: put it into the string constant pool, and take a screenshot of the operation

address.add(line.intern());



Conclusion: String pooling saves heap memory

6. Direct Memory

1. Definition

  • Common in NIO operation, it is used for data buffer
  • Allocation and recovery costs are high, but the read-write performance is high
  • It is not managed by JVM memory reclamation and belongs to system memory

2. Principle

  • General memory
    • You need to apply for resources from the user state to the inner core state, that is, the user state will create a java buffer byte[], and the kernel state will create a system buffer.
  • Direct memory
    • You need to apply for resources from the user state to the inner core state, that is, the kernel state will create a direct memory, which can be used in the user state and the kernel state.

Usually using memory (not using direct memory) VS direct memory, schematic comparison diagram

analysis:

  • Ordinary memory: because java cannot directly operate file management, it needs to switch to kernel mode, use local methods to operate, and then read disk files. A buffer will be created in the system memory, read the data to the system buffer, and then copy the system buffer data to the java heap memory. The disadvantage is that the data is stored in two copies, one in the system memory and one in the java heap, causing unnecessary replication.
  • Direct memory: direct memory is an area that can be accessed by both the operating system and Java code. There is no need to copy code from system memory to Java heap memory, which improves efficiency.

3. Comparison case of time-consuming reading large files by direct memory and traditional methods

Next, we will read a video file about 1.29G in size and write it to the specified file, that is, copy. The code is as follows:
Code reference: cn.itcast.jvm.t1.direct.demo1_ nine

/**
 * Demonstrate the role of ByteBuffer
 */
public class Demo1_9 {
    static final String FROM = "E:\BaiduNetDisk\Download\Spring video-Ma soldier assistant\SpringBoot automatic assembly.mp4";
    static final String TO = "E:\\a.mp4";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        io(); // io time: 1535.586957 1766.963399 1359.240226
        directBuffer(); // directBuffer time: 479.295165 702.291454 562.56592
    }

    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer Time:" + (end - start) / 1000_000.0);
    }

    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io Time:" + (end - start) / 1000_000.0);
    }
}

The comparison table of operation time is as follows:

Serial numberTraditional mode IODirect memory directBufferexplain
Test 118871.591 ms6335.745 msNo cache
Test 25710.124 ms5497.707 msWith cache
Test 37355.304 ms5103.806 msWith cache

4. Direct memory overflow cases

Code reference: cn.itcast.jvm.t1.direct.demo1_ ten

/**
 * Demonstrate direct memory overflow
 */
public class Demo1_10 {
    static int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
        // The method area is the jvm specification, and the implementation of the method area in jdk6 is called the permanent generation
        //                  jdk8's implementation of method area is called meta space
    }
}

Direct memory overflow


ByteBuffer has been allocating direct memory, 100M each time. After 36 times, the direct memory overflowed

5. Distribution and use principle

Code reference: cn.itcast.jvm.t1.direct.demo1_ twenty-six

/**
 *  Effect of disabling explicit recycling on direct memory
 *
 *  Because the program calls system GC () will trigger full gc, which may be garbage collected for a long time
 *  
 *  In order to prevent programmers from calling system GC (), we generally disable explicit calls to System.gc()
 *  Disable explicit system GC () will affect the direct memory. Therefore, we need to free the direct memory through the freeMemory() method of the unSafe class
 */
public class Demo1_26 {
    static int _1Gb = 1024 * 1024 * 1024;

    /*
     * -XX:+DisableExplicitGC Explicit
     */
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println("Allocation completed...");
        System.in.read();
        System.out.println("Start release...");
        byteBuffer = null;
        System.gc(); // Explicit garbage collection, Full GC
        System.in.read();
    }
}

Check the java process with 1G memory allocated through the task manager

Direct memory reclaimed through System.gc()

6. Distribution and recycling principle and case demonstration

Code reference: cn.itcast.jvm.t1.direct.demo1_ twenty-seven

/**
 * The underlying principle of direct memory allocation: Unsafe
 *
 * If the object associated with the virtual reference is recycled, the clean method of the virtual reference object will be triggered, and then the freeMemory() method of Unsafe will be called
 *
 * 6.3 Distribution and recycling principle
 *
 * The UnSafe object is used to complete the allocation and recycling of direct memory, and the recycling needs to actively call the freeMemory method
 *
 * ByteBuffer Within the implementation class of, Cleaner (Virtual Reference) is used to monitor ByteBuffer objects. Once ByteBuffer objects are garbage collected,
 * Then the ReferenceHandler thread will call freeMemory through the clean method of Cleaner to release direct memory
 */
public class Demo1_27 {
    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        Unsafe unsafe = getUnsafe();
        // Allocate memory
        long base = unsafe.allocateMemory(_1Gb);
        unsafe.setMemory(base, _1Gb, (byte) 0);
        System.in.read();

        // Free memory
        unsafe.freeMemory(base);
        System.in.read();
    }

    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);
            return unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

Run the program, allocate direct memory and release direct memory by the unsafe class

unsafe class allocates 1G direct memory

unsafe class frees direct memory

1. Principle of direct memory recovery

Direct memory recycling is not released through JVM garbage collection, but through unsafe Freememory to release manually.
Step 1: implementation of allocateDirect

public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}

The bottom layer is to create a DirectByteBuffer object.
Step 2: DirectByteBuffer class

DirectByteBuffer(int cap) {   // package-private
   
    super(-1, 0, cap, cap);
    boolean pa = VM.isDirectMemoryPageAligned();
    int ps = Bits.pageSize();
    long size = Math.max(1L, (long)cap + (pa ? ps : 0));
    Bits.reserveMemory(size, cap);

    long base = 0;
    try {
        base = unsafe.allocateMemory(size); // Request memory
    } catch (OutOfMemoryError x) {
        Bits.unreserveMemory(size, cap);
        throw x;
    }
    unsafe.setMemory(base, size, (byte) 0);
    if (pa && (base % ps != 0)) {
        // Round up to page boundary
        address = base + ps - (base & (ps - 1));
    } else {
        address = base;
    }
    cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); // this is the actual object of the virtual reference. The second parameter is a callback, which implements the runnable interface. In the run method, the memory is released through unsafe.
    att = null;
}

Here, a Cleaner's create method is called, and the background thread will also monitor the virtual reference object. If the virtual reference actual object (here is DirectByteBuffer) is recycled, the Cleaner's clean method will be called to clear the memory occupied in the direct memory.

public void clean() {
    if (remove(this)) {
        try {
            // All use the run method of functions to free memory
            this.thunk.run();
        } catch (final Throwable var2) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    if (System.err != null) {
                        (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
                    }

                    System.exit(1);
                    return null;
                }
            });
        }

    }
}

You can see the key line of code, this.thunk Run (), thunk is the Runnable object. The run method is a callback to the run method in Deallocator,

public void run() {
    if (address == 0) {
        // Paranoia
        return;
    }
    // Free memory
    unsafe.freeMemory(address);
    address = 0;
    Bits.unreserveMemory(size, capacity);
}

2. Summary of direct memory recycling mechanism

  • The UnSafe object is used to complete the allocation and recycling of direct memory, and the recycling needs to actively call the freeMemory method
  • Within the ByteBuffer implementation class, Cleaner (Virtual Reference) is used to monitor ByteBuffer objects. Once ByteBuffer objects are garbage collected, the ReferenceHandler thread will call freeMemory through Cleaner's clean method to release direct memory

3. Attention

/**
  * -XX:+DisableExplicitGC Disable displayed garbage collection
 */
private static void method() throws IOException {
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1GB);
    System.out.println("Allocation completed");
    System.in.read();
    System.out.println("Start release");
    byteBuffer = null;
    System.gc(); // Manual gc failure
    System.in.read();
}

Generally, when tuning with jvm, the following parameters will be added:

-XX:+DisableExplicitGC  // Suppressed GC

It means that we are forbidden to manually GC, such as manual system GC () is invalid. It is a kind of full gc, which will recycle the new generation and old age, and will cause the program execution time to be relatively long. So we use the unsafe object to call freeMemory to free memory.

Tags: Java jvm Interview

Posted by mike0193 on Fri, 12 Aug 2022 21:44:05 +0300