Welcome to follow our wechat official account: Shishan100
This article is authorized to transfer from the public number tancivet technology home column: “from scratch take you to become a JVM combat master”
Author: Captain of the fire brigade
Directory:
-
These reviews
-
When does a JVM load a class?
-
From a practical point of view, look at the process of validation, preparation, and initialization
-
Core phase: Initialization
-
Class loaders and parent delegate mechanisms
-
The answer to yesterday’s thought question
-
Today’s quiz question
1, the previous review
Let’s start by reviewing one of the general principles of the JVM that we talked about yesterday.
We compile it from a.java code file into a.class bytecode file, and then the class loader loads the classes in the.class bytecode file into the JVM, and then the JVM executes the code in those classes that we’ve written, in that order.
Take a look at the image below to get a feel for the process:
So today, let’s take a closer look at the “class loading” process in the figure above and see what the class loading mechanism of the JVM actually looks like.
Once you understand this process, you’ll be able to clarify some of the core concepts of the JVM class loading mechanism that interviewers often ask about in future interviews.
When does a JVM load a class?
In fact, the process of class loading is very trivial and complicated, but for us usually from the practical point of view of work, mainly to grasp his core working principle can be.
From load to use, a class typically goes through the following process:
Load -> Verify -> Prepare -> Parse -> Initialize -> Use -> Uninstall
So the first question to ask is, under what circumstances does a JVM load a class while executing our code?
That is, when the class will be loaded into JVM memory from the “. Class “bytecode file.
The answer is simple: when you use this class in your code
For a simple example, let’s say you have a class (kafka.class) that has a “main()” method as the main entry.
Once your JVM starts, it must load your class (kafka.cass) into memory and execute it from the entry code of the “main()” method.
Let’s stick to the picture one step at a time. Let’s take a look at the picture below and get a feel for it:
Then assume that in the above code, the following line of code appears:
ReplicaManager is a class in your code that needs to be used to instantiate an object. You must load the class from the “Replicamanager.class” bytecode file into memory. Isn’t it?
This triggers the JVM to load the corresponding class from the “Replicamanager.class” bytecode file into memory through the classloader, so that the code can run.
Let’s look at the picture below:
Above is to give you an example, I believe very easy to understand.
To recap: The main class in your code that contains the “main()” method must be loaded into memory after the JVM process starts to execute the code in your “main()” method
Then if you use another class, such as “ReplicaManager,” it will load the corresponding class into memory from the corresponding “.class “bytecode file.
3. From a practical point of view, look at the process of validation, preparation, and initialization
In fact, the above class loading timing problem, for many experienced students is not a problem. But for many beginners, it’s a very important concept to sort out.
Let me take you through three other concepts from a practical point of view:
Validate, prepare, initialize
In fact, for these three concepts, there is no great need to go into the details, the details here are very complicated, for most of the students, as long as there are several concepts in mind:
(1) Verification stage
Simply put, this step is to verify that the contents of the “.class “file you load conform to the specified specification against the Java virtual machine specification.
This should be easy to understand. If someone tampered with your “.class “file and the bytecodes in it didn’t conform to the specification at all, then the JVM wouldn’t be able to execute the bytecodes!
After loading “.class “into memory, it must be verified that it conforms to the JVM specification before it can be run by the JVM.
Here’s a diagram that shows the process:
(2) Preparation stage
This phase is easy to understand. As you can see, the ReplicaManager class has a number of variables, such as the following “ReplicaManager” class:
Suppose you have a “ReplicaManager” class whose “Replicamanager.class” file is just loaded into memory, and it verifies that the bytecode file’s contents are canonical.
Then, the preparation is done, which is to allocate a certain amount of memory to the “ReplicaManager” class.
Then allocate memory space for the class variable (that is, the static variable) inside it, with a default initial value.
In the example above, the class variable ‘flushInterval’ is allocated content space and given an initial value of ‘0’.
The whole process is shown in the figure below:
(3) Analysis stage
This phase is actually the process of replacing symbolic references with direct references, but this part of the process is complicated and involves the underlying JVM
But notice, students, as far as I’m concerned, I want the first week’s paper to be absolutely easy to understand, step by step, and make sure that everyone can absolutely understand it.
So for this stage, I am not going to do a deep interpretation, because from a practical point of view, for many students in the work of the practice of JVM technology is actually not needed, so here you temporarily know that there is such a stage.
Again, let me draw a picture for you:
(4) Summary of the three stages
In fact, the core of the three stages that you should pay attention to is the preparation stage.
Because at this stage, you allocate memory for the loaded class, and you allocate memory for the class variables, and you give them default initial values.
4. Core phase: Initialization
As mentioned earlier, in the preparation phase, our “ReplicaManager” class will be allocated memory space
In addition, the class variable “flushInterval” also gives a default initialization value of “0.” Then, in the initialization phase, we will execute our class initialization code.
So what is the code for class initialization? Let’s look at the following code:
GetInt (“replica.flush. Interval “) to get a value for the “flushInterval” class and assign it to it
But will this assignment logic be performed in the preparation phase?
NO! In preparation, we simply create a memory space for the ‘flushInterval’ class variable and give it an initial value of ‘0.’
So when does this assignment get executed? The answer is to do it in the “initialization” phase.
GetInt (“replica.flush.interval”) reads a Configuration item and assigns it to the class variable “flushInterval”.
Other static code blocks, such as the one shown below, are also executed in this phase.
When a class is initialized, the loadReplicaFromDish() method is called to load a copy of the data from the disk and place it in the static variable “replicas” :
So to understand what class initialization is, we have to look at the rules of class initialization.
When is a class initialized?
For example, the new ReplicaManager() will be used to instantiate the new ReplicaManager object. The new ReplicaManager() will trigger the initialization process, prepare the new ReplicaManager, and then instantiate the object.
Or a main class containing a “main()” method, which must be initialized immediately.
In addition, there is an important rule that if a class is initialized and its parent is not initialized, it must initialize its parent first
For example:
If you want “new ReplicaManager()” to initialize an instance of the class, the class will be loaded and then initialized
If AbstractDataManager is not yet loaded and initialized, the class must be loaded and initialized.
This is a rule that you have to remember. Here’s another picture to help you understand it:
Class loaders and parent delegate mechanisms
Now that you’ve figured out the whole process of class loading from firing time to initialization, let me introduce you to the concept of class loaders. Because to do that, you have to rely on the class loader.
So what class loaders are available in **Java? ** In brief, there are the following:
(1) Start the class loader
Bootstrap ClassLoader, which is mainly responsible for loading the core classes in the Java directory we installed on the machine
I believe we all know that if you want to run your own written Java system on a machine, whether Windows laptop, or Linux server, do you need to install JDK?
In your Java installation directory, there is a “lib” directory, you can go to find, there are some of the most core Java class libraries, support your Java system to run.
So once your JVM is started, the first step is to load the core libraries in the “lib” directory of your Java installation directory, relying on the startup class loader.
(2) Extend the class loader
Extension ClassLoader is a similar ClassLoader that has a “lib\ext” directory in your Java installation directory
There are some classes that you need to load using the class loader to support your system.
Does your JVM also have to load classes in the “lib\ext” directory from the Java installation directory once it starts?
(3) Application class loader
Application ClassLoader, which is responsible for loading classes in the path specified by the “ClassPath” environment variable
This class loader is responsible for loading the classes that you have written into memory.
(4) Custom class loaders
In addition to the above, you can also customize class loaders to load your classes according to your own needs.
(5) Parent delegation mechanism
The JVM class loaders have a parent-child hierarchy, meaning that the launcher class loaders are at the top, the extension class loaders are at the second, the application class loaders are at the third, and the custom class loaders are at the last.
Take a look at the picture below:
Then, based on this parent-child hierarchy, there is a mechanism for parental delegation
What does that mean?
If your application class loader needs to load a class, it will delegate the load to its parent class loader, and eventually to the top-level class loader
But if the parent class loader does not find the class in its own area of responsibility, it will push down the loading rights to its own subclass loader.
After listening to the above a lot of tongue twisters, is not very confused? Don’t worry, let’s use an example to illustrate.
For example, if your JVM now needs to load the “ReplicaManager” class, the application classloader will ask its dad, the extension classloader, can you load this class?
And then the extension class loader just asks its dad, start the class loader, can you load this class?
Start the classloader thinking, “I can’t find this class in the Java installation directory.
Then, the extension class loader is pushed down the load right to the son of the extension class loader, the result of the extension class loader looked for a long time, but did not find their responsible directory has this class.
At this point, he became angry and said, “Obviously, your application loader is responsible, you find it.
Then the application class loader is responsible for its scope, such as the jar package that you wrote the system into, suddenly found, here! It then loads the class into memory itself.
This is known as the parent-delegate model: the father loads it, and the son loads it if it doesn’t work.
This way, you can avoid a multi-tiered loader structure that reloads certain classes.
Finally, I’ll give you a picture that gives you a feel for the parent delegate model of the class loader.
6, yesterday thought the answer to the question
Good! After reading today’s article, I believe we can roughly guess the answer to yesterday’s question.
My question yesterday was: How do I handle the “.class “files without having them decompiled to get the company’s source code?
In fact, after reading today’s article carefully, it is very simple. First of all, when you compile, you can use gadgets to encrypt bytecode, obfuscate, and so on
There are many third-party companies that specialize in commercial-grade bytecode file encryption, so you can pay for their products.
Then in the class load time, for encrypted classes, consider using a custom class loader to decrypt files, so that you can ensure that your source code is not stolen.
7. Think today
Today, I will leave a question for you. I believe that every Student who does Java knows that the Web system developed in Java is generally deployed by a Web container such as Tomcat, unless it is based on Java middleware.
So if you think about it, Tomcat itself is written in Java and is itself a JVM.
The system programs we write are, in plain English, a bunch of compiled.class files that are put into a WAR package and run in Tomcat.
How should Tomcat’s class loading mechanism be designed so that classes from our dynamically deployed WAR package can be loaded into the JVM that Tomcat itself is running to execute the code we’ve written?
You think first, tomorrow at the end of the article will give you a comb and give the answer.
End