Analysis of the principle of jvm - no madness, no survival

java program running

*.java file-->compiler-->*.class file-->thread startup (main)-->jvm-->operating system-->hardware

 

Through the above process, we can see the execution order of the java program, so what is the jvm and how the class file runs in the jvm is very important.

jvm principle

what is jvm

openjdk source address http://hg.openjdk.java.net/jdk9

The JVM is a computer model, and the JVM gives clear specifications for the format of Java executable code , that is, Bytecode . This specification includes the syntax of opcodes and operands (that is, the cpu instruction set) and values, the numerical representation of identifiers , and the Java objects in Java class files, and the memory image of the constant buffer pool in the JVM.

Components of the JVM

JVM instruction system , JVM registers , JVM stack structure, JVM fragmentation recovery heap, JVM storage area

JVM instructions

Java instructions are also composed of two parts: opcodes and operands, which are consistent with the coding method used by RISC CPUs , that is, reduced instruction sets. Currently, UNIX, Linux, and MacOS systems use RISC. We currently know that the x86 architecture CPU uses CISC. Encoding, that is, a complex instruction set.

JVM registers

1.pc program counter

2. optop operand stack top pointer

3.frame current execution environment pointer

4.vars pointer to the first local variable in the current execution environment

 

jvm mount

The Windows operating system is loaded into the JVM through Java.exe in the jdk, and the JVM environment is completed through the following 4 steps.

1. Create a JVM loading environment and configuration

2. Load JVM.dll (C:\Program Files\Java\jre1.8.0_151\bin\server linux is under jre/lib/server)

3. Initialize JVM.dll and hook to the JNIENV (JNI call interface) instance

4. Call the JNIEnv instance to load and process the class class .

The JVM virtual machine is equivalent to the x86 computer system, and the Java interpreter is equivalent to the x86 CPU

JVM running data

The JVM defines several data areas that are used during program execution. Some data in this area is created when the JVM starts and destroyed when the JVM exits. Other data depends on each thread, created when the thread is created, and destroyed when the thread exits. There are program counter, heap, stack, method area, runtime constant pool respectively

  • Program Counter: Each thread has its own program counter once it is created. When a thread executes a Java method, it contains the address of the instruction that the thread is executing. But if the thread executes a local method, then the value of the program counter is not defined.
  • Constant buffer pool and method area: The constant buffer pool is used to store class names, method and field names, and string constants. The method area is used to store the bytecode of the Java method . The specific implementation of these two storage areas is not clearly specified in the JVM specification. This means that the storage layout of a Java application must be determined at runtime, depending on how the specific platform is implemented.
  • Stack: The Java stack is the JVM's primary method of storing information. When the JVM gets a Java bytecode application, it creates a stack frame for each method of a class in the code to save the method's state information. Each stack frame includes the following three types of information:

    Local variable : The corresponding vars register points to the first local variable in the variable table, which isusedto store the local variables used in the methods of a class.

    Execution environment: the current execution environment pointer corresponding to the frame register , which is used to save the information required by the interpreter to interpret the Java bytecode. These are: the last method called, the local variable pointer , and the top and bottom pointers of the operand stack. An execution environment is a control center that executes a method. For example, if the interpreter wants to perform iadd (integer addition), it first finds the current execution environment from the frame register, then finds the operand stack from the execution environment, pops two integers from the top of the stack for addition, and finally pushes the result. Push to the top of the stack.

    Operand stack: The top pointer ofcorresponding tooptopregister. Thestack is used to store the operands required for the operation and the result of the operation.

  • Heap: The largest in the JVM. The objects and data of the application are stored in this area. This area is also shared by threads and is also the main recycling area of ​​the gc. There is only one heap class for a JVM instance, and the size of the heap memory can be adjusted. of. After the class loader reads the class file, it needs to put the classes, methods, and constant variables into the heap memory to facilitate the execution of the executor.

       The garbage collection mechanism (GC) only occurs in the thread shared area, that is, the heap and method area. The stack does not need to be recycled. When the thread is destroyed, the stack is also destroyed. That is, the heap space and method area in the above figure will generate GC.

        From the above figure, it can be found that the heap space is divided into two parts:

  • Young Generation : It is further divided into Eden space. All classes are new in Eden space. From area (Survivor 0 space) and To area (Survivor 1 space). When the Eden space is used up, the program needs to create objects again. The garbage collector of the JVM will perform garbage collection (Minor GC) on the Eden space, and move the remaining objects in the Eden space to the From area. If the From area is also full, then the area is garbage collected, and then moved to the To area. Then if the To area is also full, move to the Old area.
  • Old Generation: If the area is also full, then a Major GC (FullGCC) will be generated at this time to clean up the memory of the Tenured area. If it is found that the object cannot be saved after full GC is performed in this area, an exception java.lang.OutOfMemoryError: Java heap space will be generated .
  1. The heap memory setting of the Java virtual machine is not enough, which can be adjusted by the parameters -Xms and -Xmx.
  2. A lot of large objects are created in the code and cannot be collected by the garbage collector for a long time (there are references).
  • Permanent Generation: It is a resident memory area used to store the metadata of Class and Interface carried by the JDK itself, that is to say, it stores the class information necessary for the running environment, and the data loaded into this area will not be The memory occupied by this area will be released only when the JVM is closed after being collected by the garbage collector. An exception java.lang.OutOfMemoryError: PermGen space  jdk1.8 is generated and this error will no longer be reported. Because the unloading of class information rarely occurs, it will affect the efficiency of the GC. So PermGen was split out.
  1. Program startup needs to load a large number of third-party jar packages. For example: too many applications are deployed under one Tomcat.
  2. A large number of classes generated by dynamic reflection are continuously loaded, eventually causing the Perm area to be full.

Algorithm of jvm

Because the algorithm is too long, the specific algorithm can be consulted by yourself, mainly introducing the area where the gc algorithm occurs.

Generational collection algorithm: It is composed of replication algorithm, mark/sort, mark/sweep algorithm

The replication algorithm happens in Young Generation

Mark/Collate and Mark/Sweep algorithms take place in Old Generation and Permanent Generation

java authentication jvm

The stack generally stores pointers and basic types of objects, and the access speed is faster than the heap, second only to the registers directly located in the CPU. But the disadvantage is that the size and lifetime of the data stored in the stack must be deterministic, which lacks flexibility.

Stack data can be shared


/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        int a=0;
        int b=0;
        System.out.print(a==b);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
true
Process finished with exit code 0

The compiler first processes int a = 0; first, it will create a reference to the variable a in the stack, and then look for an address with a literal value of 0. If not found, it will open an address to store the literal value of 0, and then put a points to the address of 0. Then process int b = 0; after creating the reference variable of b, since there is already a literal value of 0 in the stack, it will point b directly to the address of 0. In this way, a and b both point to 0 at the same time.

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        int a=0;
        int b=0;
        a=1;
        System.out.print("a="+a);
        System.out.print("b="+b);
        System.out.print(a==b);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" 
a=1b=0false
Process finished with exit code 0

Let a=1 again; then, b will not be equal to 1, but equal to 0. Inside the compiler, when a=1; is encountered, it will re-search whether there is a literal value of 1 in the stack, if not, re-open the address to store the value of 1; if it already exists, directly point a to this address . Therefore, changes in the value of a will not affect the value of b. 

How String str = "abc" works

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        System.out.println(str1==str2);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
true

Process finished with exit code 0
/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        str1 = "bcd";
        System.out.println(str1 + "," + str2);
        System.out.println(str1==str2);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" 
bcd,abc
false

Process finished with exit code 0

The change in assignment results in a change in the class object reference, str1 points to another new object! And str2 still points to the original object. In the above example, when we change the value of str1 to "bcd", the JVM finds that there is no address where the value is stored in the stack, so it opens up this address and creates a new object whose string value points to this address.

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";

        str1 = "bcd";

        String str3 = str1;
        System.out.println(str3);

        String str4 = "bcd";
        System.out.println(str1 == str4);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
bcd
true

Process finished with exit code 0

The reference to the object str3 points directly to the object pointed to by str1 (note that str3 does not create a new object). When str1 changes its value, create a String reference str4, and point to the new object created by str1 modifying the value. It can be found that str4 does not create a new object this time, thus realizing the sharing of data in the stack again.

Heap verification

String class 

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = "abc";
        System.out.println(str1==str2);
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
false

Process finished with exit code 0

 The above code shows that as long as new() is used to create a new object, it will be created in the heap, and its string is stored separately, even if it is the same as the data in the stack, it will not be shared with the data in the stack.

Using the method of String str = "abc"; can improve the running speed of the program to a certain extent, because the JVM will automatically decide whether it is necessary to create a new object according to the actual situation of the data in the stack. For the code of String str = new String("abc");, new objects are created in the heap, regardless of whether their string values ​​are equal, whether it is necessary to create new objects, which increases the burden of the program. This idea should be the idea of ​​flyweight mode.

Due to the nature of the String class, when the String variable needs to change its value frequently, you should consider using the StringBuffer class to improve program efficiency.

Execution time on register < stack < heap 

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args) {
        String s1 = "ja";
        String s2 = "va";
        String s3 = "java";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);
        System.out.println(s3.equals(s4));
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
false
true

Process finished with exit code 0

Isn't it very contradictory! Are you confused again?

The reason for printing false is that java overloads "+". Looking at the java bytecode, you can find that "+" actually calls StringBuilder, so using "+" actually generates a new object. so (s3 == s4) prints false

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args){
        long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虚拟机试图使用的最大内存量。
        Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例占用的内存。
        System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB");
        System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB");
    }
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails
MAX_MEMORY =1868038144(字节)、1781.5MB
TOTAL_ MEMORY = 126877696(字节)121.0MB
Heap
 PSYoungGen      total 37888K, used 3932K [0x00000000d6400000, 0x00000000d8e00000, 0x0000000100000000)
  eden space 32768K, 12% used [0x00000000d6400000,0x00000000d67d7320,0x00000000d8400000)
  from space 5120K, 0% used [0x00000000d8900000,0x00000000d8900000,0x00000000d8e00000)
  to   space 5120K, 0% used [0x00000000d8400000,0x00000000d8400000,0x00000000d8900000)
 ParOldGen       total 86016K, used 0K [0x0000000082c00000, 0x0000000088000000, 0x00000000d6400000)
  object space 86016K, 0% used [0x0000000082c00000,0x0000000082c00000,0x0000000088000000)
 Metaspace       used 3325K, capacity 4494K, committed 4864K, reserved 1056768K
  class space    used 363K, capacity 386K, committed 512K, reserved 1048576K

Process finished with exit code 0

Change the initial value of the jvm heap to a smaller value to trigger gc recycling

import java.util.Random;

/**
 * Created by liustc on 2018/4/20.
 */
public class JvmTest {

    public static void main(String[] args){
        long maxMemory = Runtime.getRuntime().maxMemory();//返回jvm试图使用的最大内存量。
        Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例的内存大小。
        System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB");
        System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB");
        String str = "www.baidu.com";
        while(true){
            str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999);
        }
    }}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails
MAX_MEMORY =1868038144(字节)、1781.5MB
TOTAL_ MEMORY = 126877696(字节)121.0MB
[GC (Allocation Failure) [PSYoungGen: 32247K->2729K(37888K)] 32247K->10124K(123904K), 0.0045031 secs] [Times: user=0.01 sys=0.03, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 32912K->4469K(70656K)] 40307K->26638K(156672K), 0.0121112 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 66160K->776K(70656K)] 88329K->59879K(156672K), 0.0141096 secs] [Times: user=0.03 sys=0.02, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 776K->0K(70656K)] [ParOldGen: 59103K->37630K(116224K)] 59879K->37630K(186880K), [Metaspace: 3408K->3408K(1056768K)], 0.0143902 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 60370K->0K(70656K)] [ParOldGen: 96726K->74565K(172032K)] 157096K->74565K(242688K), [Metaspace: 3409K->3409K(1056768K)], 0.0598124 secs] [Times: user=0.08 sys=0.00, real=0.06 secs] 
[GC (Allocation Failure) [PSYoungGen: 60382K->32K(95744K)] 1257771K->1226968K(1463808K), 0.0227293 secs] [Times: user=0.06 sys=0.01, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 32K->32K(131584K)] 1226968K->1226968K(1499648K), 0.0037586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(131584K)] [ParOldGen: 1226936K->355271K(483840K)] 1226968K->355271K(615424K), [Metaspace: 3409K->3409K(1056768K)], 0.1616835 secs] [Times: user=0.19 sys=0.09, real=0.16 secs] 
[GC (Allocation Failure) [PSYoungGen: 2499K->32K(158208K)] 1303306K->1300838K(1526272K), 0.0037952 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 32K->32K(158208K)] 1300838K->1300838K(1526272K), 0.0036491 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(158208K)] [ParOldGen: 1300806K->473463K(622080K)] 1300838K->473463K(780288K), [Metaspace: 3409K->3409K(1056768K)], 0.1641897 secs] [Times: user=0.30 sys=0.06, real=0.16 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(250880K)] 946230K->946230K(1618944K), 0.0027229 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] 946230K->946230K(1626624K), 0.0027747 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] [ParOldGen: 946230K->709846K(879104K)] 946230K->709846K(1137664K), [Metaspace: 3409K->3409K(1056768K)], 0.1013768 secs] [Times: user=0.28 sys=0.02, real=0.10 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(353280K)] 709846K->709846K(1721344K), 0.0049384 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
[PSYoungGen: 0K->0K(353280K)] [ParOldGen: 709846K->709816K(900608K)] 709846K->709816K(1253888K), [Metaspace: 3409K->3409K(1056768K)], 0.1792920 secs] [Times: user=0.39 sys=0.00, real=0.18 secs] 
Heap
	at java.util.Arrays.copyOf(Arrays.java:3332)
 PSYoungGen      total 353280K, used 14028K [0x00000000d6400000, 0x00000000ec700000, 0x0000000100000000)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
  eden space 352768K, 3% used [0x00000000d6400000,0x00000000d71b3070,0x00000000ebc80000)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
  from space 512K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000ec700000)
	at java.lang.StringBuilder.append(StringBuilder.java:208)
  to   space 4608K, 0% used [0x00000000ebe00000,0x00000000ebe00000,0x00000000ec280000)
	at JvmTest.main(JvmTest.java:15)
 ParOldGen       total 1368064K, used 709816K [0x0000000082c00000, 0x00000000d6400000, 0x00000000d6400000)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  object space 1368064K, 51% used [0x0000000082c00000,0x00000000ae12e1e8,0x00000000d6400000)
 Metaspace       used 3440K, capacity 4494K, committed 4864K, reserved 1056768K
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  class space    used 377K, capacity 386K, committed 512K, reserved 1048576K
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Process finished with exit code 1

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324961819&siteId=291194637