Writing in the front

There is a great deal of pain about the Java class loading mechanism. Because when I was learning this aspect of the content, more or less some meng forced, so this article, as far as possible to the conceptual things into easy to understand the content, so I hope you can see the article’s children’s shoes can gain ~

# Start of text

First, let’s look at some code:


public class Main {
	static{
		System.out.println("I'm a static code block.");
	}
	
	{
		System.out.println("I'm an example code block.");
	}
	
	public static void main(String[] args) {
		Main main1=new Main();
		Main main2=newMain(); }}Copy the code

What does this code look like when it’s run? I don’t want to keep you in suspense here.

OK, if you know this result and you understand this result, then you can skip the rest of this. If in doubt, let’s take the answer and read on, very little. Focus on understanding ~


Java class loading mechanism

Let’s look at the concept

The virtual machine loads the data describing the class from the class file to the memory, verifies, transforms, and initializes the data, and finally forms Java types that can be directly used by virtual machines. This is the class loading mechanism of virtual machines. (Source: In-depth Understanding of the Java Virtual Machine. Second Edition, hereinafter referred to as In-depth JVM)

Concepts are always boring, so let’s start with some easy to understand analysis of this concept.

comb

1, load,

The first is the load phase: this phase is when Java removes bytecode (.class files) data from various data sources (our JAR files, class files, even network data sources, etc.); As long as the structure is correct) is read into the JVM and mapped to a JVM approved data structure (a Class object, understandably java.lang.class). As described in Into the JVM, there are three steps to this process (with some word processing) :

  • Get the binary byte stream that defines a class by its fully qualified name. (Find the.class file of the class first.)
  • 2. Convert the code structure represented by the.class byte stream into the runtime data structure of the method area. (It can be understood that the code structure in the virtual machine has been recognized.)
  • Generate an in-memory java.lang.Class object that represents the Class as an entry point to the Class’s data in the method area. This is confusing, but my understanding is that the code structure already exists, but there is no way to use it directly. So the java.lang.Class object is abstracted as the interface to the bytecode of the orientation.Class file.

I was a little confused here about bytecode and binary files. In fact, they are.class files. Let’s briefly compile the above main.java file

First we move to the directory where Main is, compile and look at the bytecode for.class.

Tips: During the loading phase, we can customize the class loader to implement our own class loading process.

2, connection

The connect and load processes intersect, meaning that the connect phase may have started before the load phase has finished.

The second stage is wiring, which is the core step and simply translates the original class definition information smoothly into the JVM running process. This can be further broken down into three steps:

  • 1. Validation: This is an important guarantee of virtual machine security. The JVM needs to verify that byte information is in compliance with the Java Virtual Machine specification.
  • 2,To prepare: Creates a static variable in a class or interface and initializes its initial value.The focus of initialization here is to allocate the required memory space, no assignmentThat is, the value initialized here is the default value, for examplepublic static int value = 666, value is 0 instead of 666. The real assignment is inInitialize thePhase.
  • 3. Parsing: In this step, symbolic references in the constant pool are replaced with direct references. This short paragraph took me a long time to understand because it contained a large number of concepts: constant pools, symbolic references, direct references. Let’s explain it point by point:

Constant pool: In addition to strings, final constants and symbolic references are included in the constant pool. Symbol reference: used to describe fields, methods, interfaces, etc., in a bytecode file. I understand it this way: if fields and methods are to imagine tourists who want to travel, then symbolic references are travel companies, but travel companies are only responsible for collecting money and organizing tourists, they are not responsible for actually taking tourists out, it is the tour guide who really takes them to play (direct reference). That is, a symbolic reference is a role that represents all fields and methods. Direct reference: Direct reference is thought of as a role that can find the corresponding memory address, which is the tour guide in the above example.


PS: I don’t know this explanation can understand parsing process, if still confused, you can view zhihu bosses professional answer to this: https://www.zhihu.com/question/30300585

3. Initialization

The last step is the initialization phase, where static field assignment is performed, and the logic inside the block is statically initialized. The compiler has already sorted out the code logic for this execution at compile time. Note that the initialization logic of the parent class takes precedence over the logic of the child class.

There is one detail to note here: only variables defined before the static code block can be accessed from the static code block. If the static variable comes after the static block, it can only be assigned to, not accessed:


End of the load

All the way down here, our Class is officially loaded, and we’ve generated our corresponding Class object. Note, however, that there is no instantiation of the class involved, meaning that the new operation has not yet started.

When new is executed, and the class has already been loaded, the instantiation is performed, such as allocating memory, executing code blocks, constructors, etc. (if there is a parent class, this is done first).

An operation that triggers a class load

  • New keyword; Get /set a static variable (final, except for static fields entered into the constant pool at compile time); Call the static method.
  • With reflection, the class is loaded first if it has not been loaded.
  • The class corresponding to the main method is loaded at JVM startup.
  • When using some dynamic proxy methods.

Parent delegation mechanism

MDove’s article is simple and clear about the parent delegation mechanism, which is actually a picture:

In the words of Into the JVM, the parent delegate model works: If a class loader receives a load request for a class, it first does not attempt to load the class itself. Instead, it delegates the request to the parent class loader, at every level of class loader. Therefore, all load requests should eventually be passed to the top level of the bootstrap class loader, and the child loader will only attempt to load itself if the parent loader reports that it cannot complete the load request.

The end of the

This is the end of the class loading comb, I don’t know if you guys understand? If you have your own understanding, or if there is something inappropriate in the article, please leave a comment in the comment section.

I am a fresh graduate, recently maintained a public account with friends, the content is that we are in the transition from fresh graduate to the development of this way stepped on the pit, has been our step by step learning records, if interested in friends can pay attention to it, together with fuel ~