“This is the 15th day of my participation in the August Gwen Challenge.

Introduction to Java and JVM

(I) The concept of Java

Java is a compiled + interpreted high-level language.

Java is not a compiled + interpreted language because javac compiles Java source code into class files, because the resulting class files cannot run directly on the corresponding platform after this compilation.

Why is Java a compilation + interpreted language? Because class files are ultimately translated by the JVM to run on the appropriate platform, this translation is mostly interpreted, but there is also compilation, called runtime compilation, or JIT (Just In Time).

To sum up, Java is a compiled + interpreted high-level language.

(ii) The concept of JVM

1, source code to class files

Class files to the JVM

3. All sorts of JVMS (internal structure, execution, garbage collection, local calls, etc.)

(iii) Some characteristics of JVM

1. The Java Virtual Machine (JVM) is the cornerstone of the Java platform. It is responsible for its hardware and operating system independence, its compiled code is small, and its ability to protect users from malicious programs.

2. The Java Virtual machine is an abstract computer that, like a real computer, has an instruction set and manipulates various memory regions at run time. (Following the von Neumann computer architecture)

3. The Java Virtual machine is not responsible for any specific implementation technology, host hardware, or host operating system, and is not itself explained.

4. The Java virtual machine does not know Java as a language, but only the specific binary format, the class file format, which contains Java virtual machine instructions (or bytecodes), symbol tables, and other auxiliary information.

5. For security reasons, the Java Virtual Machine imposes strong syntactic constraints on code in class files, but any language with functionality that can be represented in a valid class file can be hosted by the Java Virtual Machine, attracted by a generic, machine-independent platform. Implementers of other languages can use the Java Virtual Machine as a delivery tool for their languages.

Two, source code to the class file

(a) source demo

class Person{
	private String name="Jack";
  	private int age;
	private final double salary=100;
  	private static String address;
  	private final static String hobby="Programming";

	private static Object obj=new Object();
  	public void say(a){
    	System.out.println("person say...");
 	}
  	public static int calc(int op1,int op2){
    	op1=3;
    	int result=op1+op2;
		Object obj=new Object();
    	return result;
 	}
	public static void main(String[] args){
	calc(1.2); }}Copy the code

(2) Compiler operations

Javac -g:vars person.java — > person.class

Person.java — > (Parser) — > Tokens stream — > (Parser) — > Syntax tree/Abstract syntax tree — > (Semantic parser) — > Annotation Abstract syntax tree — > (bytecode generator) — > Person.class file

In fact, what the compiler does is “peer to peer information conversion”. The information in a Java file is the same as the information in a class file.

(3) Class file (class file)

1. Part of hexadecimal class file

cafe babe 0000 0034 003f 0a00 0a00 2b08
002c 0900 0d00 2d06 4059 0000 0000 0000
0900 0d00 2e09 002f 0030 0800 310a 0032
0033 0700 340a 000d 0035 0900 0d00 3607
0037 0100 046e 616d 6501 0012 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 0100
0361 6765 0100 0149 0100 0673 616c 6172
7901 0001 4401 000d 436f 6e73 7461 6e74.Copy the code

2. The ClassFile Structure

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
Copy the code

The above source website (docs.oracle.com/javase/spec…

3. Simple analysis

U4: cafe babe

magic:The magic item supplies the magic number identifying the class file
format
Copy the code

U2 + U2:0000 0034 — 34 is equal to base 10 of 52, representing JDK8

minor_version
major_version
Copy the code

U2:003f = 63 (base 10) means that the quantity in the constant pool is 62

constant_pool_count:
The value of the constant_pool_count item is equal to the number of entries
in the constant_pool table plus one.
Copy the code

cp_info constant_pool[constant_pool_count-1]

The constant_pool is a table of structures representing various string
constants, class and interface names.field names.and other constants that
are referred to within the ClassFile structure and its substructures. The
format of each constant_pool table entry is indicated by its first "tag"
byte.
The constant_pool table is indexed from 1 to constant_pool_count1.Copy the code

The constant pool stores two main things: literals and Symbolic References.

Literals: text strings, final modifiers, etc

Symbolic references: fully qualified names of classes and interfaces, field names and descriptors, method names and descriptors

(4) Decompilation verification

1. Result of decompilation operation

Verify the above conjecture using javAP instructions

Javap -v -p person.class

The JVM can be understood as an operating system in relation to a class file; Class files can be interpreted as assembly language or machine language relative to the JVM

2. Decompilation operation analysis

The number of constants in the constant pool analyzed above is 58, Cp_info constant_pool[constant_pool_count-1] cp_info is the form of All constant_pool table entries have the following general format:

cp_info {
u1 tag;
u1 info[];
}
Copy the code

(1) The lower number u1, i.e. 0a->10, represents CONSTANT_Methodref, indicating that this is a method reference

CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Copy the code

(2) u2 and U2 u2, i.e. 00 0a->10: represents class_index, which indicates the constant pool index u2 of the class to which this method belongs, i.e. 00 2b->43: represents name_AND_type_index, which indicates the name and type index of this method

#1 = Methodref    #10#,43
Copy the code

(3) The lower number U1, that is, 08->8, is the string type CONSTANT_String

CONSTANT_String_info {
u1 tag;
u2 string_index;
}
Copy the code

(4) u2, 00 2c->44: represents string_index

#1 = Methodref    #10#,43
#2 = String     #44
Copy the code

(5) The lower number U1, that is, 09->9, is CONSTANT_Fieldref, indicating the field type

CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Copy the code

(6), the bottom number u2 and U2 u2, namely 00 0D ->13: represents class_index U2, namely 00 2D ->45: represents name_AND_type_INde

#1 = Methodref    #10.#43
#2 = String     #44
#3 = Fieldref    #13.#45
Copy the code

Class loading mechanism

(1) The concept and method of class loading mechanism

Class loading is a special mechanism whereby we read the data contained in the class’s bytecode file into memory and generate an access point for the data. So we know that the end product of class loading is data access.

  • How to load a. Class file
    • Load directly from the local system
    • Download the. Class file from the network
      • Typical scenario: A Web Applet, our small application
    • Load. Class files from zip, JAR, etc archives
      • Typical scenario: The jar and WAR formats are subsequently changed
    • Advance. Class files from a proprietary database
      • Typical scenario: JSP applications extract. Class files from a proprietary database, which is rare.
    • Dynamically compile Java source files into.class files, which are computed at runtime
      • Typical scenario: Dynamic proxy technology
    • Obtained from an encrypted file
      • Typical scenario: Typical protection measures against Class files being decompiled

(2) Loading of the class loading process

Use and unload are not phases of the class loading process

Load: Finds and imports class files

(1) Get the binary byte stream that defines this class by a fully qualified radar name (we do not necessarily get it from the bytecode file)

  • A tool, finder, is needed to retrieve the binary byte stream.
  • The Java class is made up of just such a block of code. The action of getting the binary stream of a class by its fully qualified name can be implemented outside the Java virtual machine so that the application can decide how to get the required class. The code module that implements this action is called the class loader.

(2) Convert the static storage structure represented by this byte stream to the runtime data structure of the method

(3) Generate a java.lang.Class object representing the Class in the Java heap as an access point to the heap method

The phase of getting the binary byte stream of a class is the one we JAVA programmers care about the most, and the most manipulative. Because at this stage we can operate on our classloader, for example we want to customize the classloader to do the loading, or we want to use the JAVA Agent to do our bytecode enhancement.Copy the code

After our load phase is complete, at this point in our memory, our run-time data area method area and heap will already have data.

  • Method area: class information, static variables, constants
  • Heap: Java.lang. Class object representing the loaded Class

Just-in-time compiled hot code does not enter the method area at this stage

(3) links to the process of class loading

1, validation,

The verification is to ensure that the byte stream in the Class file contains information that meets the requirements of the current VIRTUAL machine, and that the information does not harm the security of the virtual machine and cause the virtual machine to crash.

  • File format validation
    • Verify that the byte stream complies with the Class file format specification and can be processed by the current version of the virtual machine. The main purpose of this verification is to ensure that the input byte stream is properly parsed and stored in the method area. The verification in this stage is based on the binary byte stream. Only after the verification in this stage, the byte stream will enter the method area of memory for storage. The subsequent verification is based on the storage structure of the method area.
    • For example:
      • Whether to start with cafebaby in hexadecimal
      • Check whether the version number is correct
  • Metadata validation
    • Semantic verification (in fact, Java syntax verification) is performed on the metadata information of a class to ensure that there is no metadata that does not conform to Java syntax specifications.
    • For example:
      • Is there a parent class
      • Whether a final class is inherited
        • Because our final class cannot be inherited, there will be problems if we do.
      • Whether a non-abstract class implements all abstract methods
        • If there is no implementation, then the class is also invalid.
    • Semantic verification (in fact, Java syntax verification) is performed on the metadata information of a class to ensure that there is no metadata that does not conform to Java syntax specifications.
  • Bytecode verification
    • Perform data flow and control flow analysis to determine program semantics are legitimate and logical. The method body of a class is verified and analyzed to ensure that the methods of the verified class do not harm vm security during running.
    • For example,
      • Run to check
      • The stack data type matches the opcode operation parameter (e.g. the stack space is only 4 bytes, but we need more than 4 bytes, then the bytecode is problematic)
      • The jump instruction points to a reasonable location
      • Bytecode verification can be relatively complex.
  • Symbolic reference verification
    • This is the final phase of validation, which occurs when the virtual machine converts symbolic references to direct references (the parsing phase), and can be viewed as a verification of match for information outside the class itself (various symbolic references in the constant pool). The purpose of symbolic reference validation is to ensure that the parsing action is performed properly.
    • For example,
      • The constant pool describes whether the class exists
      • Whether the method or field being accessed exists and has sufficient permissions

2, preparation,

  • Allocates memory for class variables (static variables) and sets default initial values for the class variables

Int 0 long 0L short (short)0 char ‘\u0000’ byte (byte)0 Boolean false float 0.0f double 0.0D reference null

  • Static with final is not included here, because final is allocated at compile time and initialized explicitly during preparation;

  • Class variables are allocated in the method area, while instance variables are allocated in the Java heap along with the object.

The Demo code

 public class Demo1 {
 private static int i;
 public static void main(String[] args) {
   // Print 0 normally, because the static variable I has a default value of 0 during the preparation phaseSystem.out.println(i); }}Copy the code
public class Demo2 {
 public static void main(String[] args) {
   However, local variables cannot be used because there is no assignment
   inti; System.out.println(i); }}Copy the code

Allocated memory includes only class variables (static variables), not instance variables, which are allocated in the Java heap along with the object when it is instantiated. Public static int a=1; public static int a=1; Then the initial value of A after the preparation phase is 0, not 1. At this time, only memory space is opened, and Java code is not run. The instruction assigned to a value of 1 is stored in the class constructor () method after the program is compiled, so the instruction assigned to A value of 1 is executed during the initialization phase. In some special cases, if a ConstantValue attribute is present in the class field attribute table, variable a is initialized to the value referred to by the ContstantValue attribute during the preparation phase. What do we make of this statement? If we look at our decompiled file, we can see that there is such a property.

Consider: what is the ConstantValue attribute for?

The ConstantValue property notifies the VM to automatically assign a value to a static variable. Only static variables can use this property. Assignments to variables of non-static type are made in instance constructor methods; Static variable assignment is either in a class construct or using the ConstantValue attribute.

Consider: When would we use the ContstantValue attribute in a real application?

In real programs, only fields that are both final and static will have ConstantValue, and are limited to primitive types and Strings. At compile time, Javac will generate a ConstantValue attribute for this constant. In preparation for the class load, the virtual machine will set a ConstantValue based on ConstantValue. If the variable is not modified by final, or is not a basic type and string, it will be initialized in the class constructor.

Consider: why is ConstantValue limited to primitive types and strings?

Because only primitives and String literals can be referenced from the constant pool

So at this point, let’s take an example:

Private static final int a = 1; private static final int a = 1; The Javac will generate ConstantValue for A at compile time, and in preparation the VM will assign value to 1 based on the ConstantValue setting. Static final constants put their results into the constant pool of the calling class at compile time

3, parsing,

  • Converts symbolic references in a class to direct references
    • A symbolic reference is a set of symbols that describe a target, which can be any literal. The referenced target is not necessarily already loaded into memory.
    • A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly.
  • The parsing phase is the process by which the virtual machine replaces symbolic references in the constant pool with direct references.
  • The parse action is mainly for class or interface, field, class method, interface method, method type, method handle, and call qualifier class symbol references
  • A direct reference is related to the implementation of virtual machine memory layout. The translation of a symbolic reference on different VIRTUAL machine instances is generally different. If there is a direct reference, the target of the reference must exist in memory.
  • The result of parsing is cached
    • Multiple parsing requests for the same symbolic reference are common, and with the exception of invokedynamic instructions, virtual machine implementations can cache the results of the first parsing to avoid repeated parsing. Whether or not multiple resolution actions are actually performed, the virtual machine needs to ensure that if a reference symbol has been successfully resolved before, subsequent reference resolution requests within the same entity should always be successful. Similarly, if the first parse fails, all other instructions parsing requests for the symbol should receive the same exception.
    • InDy (InvokeDynamic) is a new virtual machine instruction introduced in Java 7 for the first time since 1.0. It was in Java 8 that this instruction was first used in Java, in lambda expressions. Indy differs from other Invoke directives in that it allows application-level code to determine method resolution.

(4) Initialization

  • The initialization phase is the process of executing the class constructor () method.

    • Or make it easy to understand
    • In the preparation phase, class variables have been assigned the initial values required by the system. In the initialization phase, class variables and other resources are initialized according to a subjective plan made by the programmer through the program, such as assignment.
  • There are two ways to initialize a class variable in Java:

    • Declare class variables to specify initial values
    • Use static code blocks to specify initial values for class variables
    • Static variables need to be defined in front of static code blocks. Because the execution of the two is determined by the order in which the code is written, the wrong order can affect your business code.
  • JVM initialization steps:

    • If the class has not already been loaded and connected, the program loads and connects the class first
    • If the class’s immediate parent has not already been initialized, its immediate parent is initialized first
    • If there are initializers in a class, the system executes those initializers in turn

Four, use and uninstall

(1) Use

  • When will our initialization be triggered to execute? Or to put it another way what is the class initialization timing?
  • Active reference
    • Class initialization occurs only when active use of a class is made. There are six types of active use of a class:
      • Create an instance of the class, which is new
      • Accesses or assigns a value to a static variable of a class or interface
      • Call a static method of a class
      • Reflection (e.g. Class.forname (” com.carl.test “))
      • If you initialize a subclass of a class, its parent class is initialized as well
      • A class (JvmCaseApplication) that is marked as a startup class when the Java virtual machine starts, running a main class directly using the java.exe command
  • Passive reference
    • A reference to a static field of a parent class only causes initialization of the parent class, not of the child class.
    • Defining an array of classes does not cause class initialization.
    • Referring to a class’s static final constant does not cause the class to be initialized (it would still cause the class to be initialized if there were only static modifications).

(2) Unloading

After a class is used, it is unloaded if:

  • All instances of the class have been reclaimed, meaning that there are no instances of the class in the Java heap.
  • The ClassLoader that loaded the class has been reclaimed.
  • The java.lang.Class object corresponding to this Class is not referenced anywhere, and the methods of this Class cannot be accessed through reflection anywhere.

The Java virtual machine itself always references these Class loaders, and these Class loaders always reference the Class objects of the classes they load, so these Class objects are always accessible.

If all three conditions are met, the JVM will unload the class at the time of the method area garbage collection. The process of unloading the class is to empty the method area of the class information, and the entire life cycle of the Java class is over. But in general the classes loaded by the launcher classloader are not unloaded, and our other two base types of classloaders are only unloaded in rare cases.

Class loaders

What is a class loader

  • The code module responsible for reading Java bytecode and converting it into an instance of the java.lang.Class Class.
  • In addition to loading classes, class loaders can also be used to determine the uniqueness of classes in the Java virtual machine.

A class has Uniqueness in the same classloader, whereas different classloaders allow classes with the same name, which means full Uniqueness of the same name. However, even though the fully qualified names are the same across the JVM, if the class loaders are different, they are not counted as the same class and cannot be checked by instanceOf, Equals, etc.

(1) Bootstrap ClassLoader

Load all jar packages specified by class or Xbootclassoath in jre/lib/rt.jar in JAVA_HOME. Implemented by C++, not a ClassLoader subclass.

(2) Extension ClassLoader

It is responsible for loading some JAR packages of extended functions in Java platform, including jre/lib/*. Jar in $JAVA_HOME or jar packages in -djava.ext. dirs specified directory.

(3) App ClassLoader

Is responsible for loading jars specified in classpath and classes and JARS specified in djava.class. path.

(4) Custom ClassLoader

Classes can be customized by subclasses of java.lang.ClassLoader, which are customized by application programs according to their own needs. For example, Tomcat and JBoss implement ClassLoader by themselves according to j2ee specifications.

(2) Why class loaders need to be layered

In the 1.2 JVM, there was only one classloader, now the “Bootstrap” classloader. The root class loader. But there’s a problem with that.

Suppose the user calls the java.lang.String class he wrote. This class theoretically has the ability to access and change properties and methods of default access modifiers for other classes under the java.lang package. That is, our other classes will also call this class when they use String, because there is only one classloader and I can’t decide which one to load. This is problematic because the Java language itself does not prevent this behavior.

At this point, we thought, can we use different class loaders to differentiate our trust level?

For example, using three basic classloaders as our three different trust levels. The most trusted level is the Java core API class. Then the installed extension classes, and finally the classes in the classpath (classes that belong to your native machine).

Thus, our three basic class loaders were born. But that’s our perspective as developers

public class Demo3 {
  public static void main(String[] args) {
    // App ClassLoader
    System.out.println(new Worker().getClass().getClassLoader());
    // Ext ClassLoader
    System.out.println(new
Worker().getClass().getClassLoader().getParent());
    // Bootstrap ClassLoader
    System.out.println(new
Worker().getClass().getClassLoader().getParent().getParent());
    System.out.println(newString().getClass().getClassLoader()); }}Copy the code
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@3a71f4dd
null
null
Copy the code

(3) Three ways of JVM class loading mechanism

1. Take full responsibility

  • When a Class is loaded by a Class loader, other classes that that Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loader
  • For example, when the system class loader AppClassLoader loads an entry class (a class containing the main method), it loads the classes that the main method depends on, the classes that reference it, and so on. The “full responsibility” mechanism can also be called the current classloader responsibility mechanism. Obviously, the current class loader for the class that the entry class depends on and references is the class loader for the entry class.
  • The above steps just call the classLoader.loadClass (name) method without actually defining the class. The actual loading of class bytecode files to generate class objects is done by the “parent delegate” mechanism

2. Parent delegate

  • “Parent delegate” means that the subclass loader delegates the parent class loader to load the target class if it has not already loaded the target class, and only looks up and loads the target class from its own classpath if the parent class loader cannot find the bytecode file.

  • The parent delegate alias is called the parent delegate mechanism.

  • The parent delegate mechanism loads a Class as follows:

    • 1. The ClassLoader checks whether the Class has been loaded. If so, the ClassLoader returns the Class object. If not, delegate to the parent class loader.
    • 2. The parent Class loader determines whether the Class has been loaded, and returns the Class object if so. If not, delegate to the grandfather classloader.
    • 3, and so on until the ancestor class loader (reference class loader).
    • The ancestor Class loader determines whether the Class has been loaded. If so, it returns the Class object. If not, try to find the class bytecode file from its corresponding classpath and load it. If the load is successful, the Class object is returned; Delegate to the subclass loader of the ancestor class loader if the load fails.
    • 5. The subclass loader of the ancestor class loader tries to find and load the class bytecode file from its corresponding classpath. If the load is successful, the Class object is returned; If the load fails, delegate to the grandchild classloader of the ancestor classloader.
    • 6. And so on until the source ClassLoader.
    • 7. The source ClassLoader tries to find the class bytecode file from its corresponding classpath and load it. If the load is successful, the Class object is returned; If the load fails, the source ClassLoader does not delegate its subclass loader, but instead throws an exception.
  • The “parent delegate” mechanism is recommended by Java, not mandatory.

  • We can inherit the java.lang.ClassLoader class and implement our own ClassLoader. If you want to keep the parent delegate model, you should override the findClass(name) method; If you want to break the parent delegate model, you can override the loadClass(name) method.

Caching mechanism

The caching mechanism will ensure that all loaded classes are cached in memory. When a program needs to use a Class, the Class loader first looks for the Class in the memory cache. Only when the cache does not exist, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. This is why after Class changes are made, the JVM must be restarted for the program changes to take effect. For a class loader instance, classes with the same full name are loaded only once, meaning that the loadClass method is not called repeatedly.

In this case, we use direct memory in JDK8, so we will use direct memory for caching. This is why our class variable is initialized only once.

protectedClass<? > loadClass(String name,boolean resolve)
 throws ClassNotFoundException
{
 synchronized (getClassLoadingLock(name)) {
   // First, check the virtual machine memory to see if this class has been loaded... The main problem with class caching!!Class<? > c = findLoadedClass(name);if (c == null) {
     long t0 = System.nanoTime();
     try {
       if(parent ! =null) {
// Let the previous loader do the loading first
         c = parent.loadClass(name, false);
      } else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
       // ClassNotFoundException thrown if class not found
       // from the non-null parent class loader
    }
     if (c == null) {
// Call the findClass method implemented by this class loader to loadc = findClass(name); }}if (resolve) {
     // the resolveClass method is used when the bytecode is loaded into memory to link to the file format and bytecodeCard, and forstaticField space allocation and initialization, symbol reference to direct reference, access control, method overwrite, etc. ResolveClass (c); }returnc; }}Copy the code