preface
JVM is both a senior and basis for Java programmers, entered the students don’t have to know the JVM memory division, do not need to know the loading process, the recycling process of GC can comfortably write code, but this much I do not know how attitude will limit our upside, today this article into the JVM, Let’s start with the first step, figuring out how classes are loaded, and that’s what we’re going to share today!
The composition structure of the JVM
The JVM is composed of four parts: the class-loading subsystem, the runtime data area, the execution engine, and the local method interface.
Class loader
- BootstrapClassLoader (BootstrapClassLoader) : provided by c++, mainly responsible for loading
{% JAVA_HOME} \ jdk1.8.0 _261 \ jre \ lib
Classes in directories; - ExtClassLOader:
sun.misc.Launcher.ExtClassLoader
, is mainly responsible for loading{% JAVA_HOME} \ jdk1.8.0 _261 \ jre \ lib \ ext
Classes in directories; - Application class loaders:
sun.misc.Launcher.AppClassLoader
Is responsible for loading our configured environment variablesclasspath
Classes in directories; - Custom class loaders: through inheritance
ClassLoader
Classes can implement their own custom class loading methods, which can be used to break the parent-parent delegation model (discussed below);
We can clearly see what each class loader looks like in this code:
public class TestClassLoader {
public static void main(String[] args) { System.out.println(Object.class.getClassLoader()); System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader()); System.out.println(TestClassLoader.class.getClassLoader()); }}Copy the code
Output result:
null
sun.misc.Launcher$ExtClassLoader@77459877
sun.misc.Launcher$AppClassLoader@18b4aac2
Copy the code
Why is the loader corresponding to the Object class null? As mentioned above, the JVM creates a startup class loader written in c++ to load the classes in %{JAVA_HOME}\jdk1.8.0_261\jre\lib. This loader is not available in Java, so it is null. The ExtClassLoader and AppClassLoader are static inner classes of the Sun.misc.Launcher class. The Launcher class is obtained by C++ calling sun.misc.Launcher#getLauncher;
Relationships between class loads
How do class loaders relate to each other
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
System.out.println("the bootstrapLoader : " + bootstrapLoader);
System.out.println("the extClassloader : " + extClassloader);
System.out.println("the appClassLoader : " + appClassLoader);
Copy the code
Output result:
the bootstrapLoader : null
the extClassloader : sun.misc.Launcher$ExtClassLoader@77459877
the appClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2
Copy the code
As you can see, the default class loader is AppClassLoader, then ExtClassLoader, and BootStrapClassLoader. Let’s take a look at the class diagram structure of the class loader:
Class loaders are inherited classloaders and maintain a parent attribute for each subclass:
Let’s focus on what the parent property does: it initializes the app and ext objects in the constructor when we instantiate the Launcher:
- The above code does two things:
- Create an ExtClassLoader and get var1;
- Create AppClassLoader and pass var1 to it.
Click on the AppClassLoader to see how it is created. I will skip the middle doll code, which calls the super(parent) constructor directly.
This is where the parent-child relationship between class loaders is maintained, so Ext is also the parent loader of the App, so what exactly does it do? There is a concept involved here: parental delegation (more on that below); The above code also reflects a question: is the default class loader AppClassLoader? The getClassLoader() method of the Launcher class is called inside the JVM by default to get a default classLoader to load, and this classLoader happens to be the AppClassLoader generated when the Launcher class is instantiated:
Parental delegation model
When a class is loaded by the class loader, the class loader will not load it immediately. Instead, it will find the parent loader for loading through the parent attribute and recursively search until the BootStrap on the top layer is not loaded. The class loader will return to its own class loader for loading:
In addition to maintaining the parent attribute, the ClassLoader also maintains a public loadClass method, which is the implementation of the parent delegate. Let’s analyze it in detail:
protectedClass<? > loadClass(String name,boolean resolve)
throws ClassNotFoundException
{
synchronized(getClassLoadingLock(name)) { Class<? > c = findLoadedClass(name);if (c == null) {
long t0 = System.nanoTime();
try {
if(parent ! =null) {
// The parent of app is ext, and the parent of ext is bootstrap
c = parent.loadClass(name, false);
} else {
// This method ends up calling a native method that returns null if the class cannot be loaded
// return null if not foundc = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// The parent loader is not loaded into the class, return null
if (c == null) {
long t1 = System.nanoTime();
// Call the current class loader to load
c = findClass(name);
// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
resolveClass(c);
}
returnc; }}Copy the code
If you look at the code above, it’s pretty clear that we’re going to recursively call up and if we don’t load it back down;
- Why design the parent delegate model to load classes?
- Ensure that a class is loaded in memory only once;
- Protected JDK internal class security, prevent external damage;
The first point should be clear to everyone. What does the second point mean? Let’s run a code to demonstrate:
// Override the original java.lang package
package java.lang;
// Override the original Object class
public class Object {
public static void main(String[] args) {
System.out.println("object ...."); }}Copy the code
What happens after the above code is executed?
Error: Main method not found in java.lang.Object class, define main as:public static void main(String[] args)Otherwise deployment headaches the application class must extend deployment headaches. Application. The applicationCopy the code
So, because of parental delegation, we can’t maliciously break the old API;
Custom class loaders
We can customize class loaders to specify which classes we want to load. The parent delegate logic is in the loadClass method, while the parent delegate logic is in the findClass method. If I want to implement a ClassLoader myself, I should inherit the ClassLoader and override the findClass method. To load our own specified class in the findClass method:
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\ \."."/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
@Override
publicClass<? > loadClass(String name)throws ClassNotFoundException {
try {
byte[] bytes = loadByte(name);
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
throw newClassNotFoundException(); }}public static void main(String[] args) throws Exception {
// to initialize the custom ClassLoader, the parent ClassLoader is initialized first, which sets the parent of the custom ClassLoader to the application ClassLoader AppClassLoader
MyClassLoader classLoader = new MyClassLoader("D:/test");
// Put a user. class file under this path, which will be loaded by our own class loader
Class clazz = classLoader.loadClass("com.maolin.User");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout".null);
method.invoke(obj, null); System.out.println(clazz.getClassLoader().getClass().getName()); }}Copy the code
Run, output result:
com.maolin.MyClassLoader
Copy the code
The bytecode of user. class is loaded by the MyClassLoader; We can also break the parent delegate mechanism by overwriting findClass and loadClass:
@Override
protectedClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException {
synchronized(getClassLoadingLock(name)) { Class<? > c = findLoadedClass(name);/* We copy the code in the loadClass method of the ClassLoader class and remove the parent delegate
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
returnc; }}Copy the code
- When do you break parental delegation
- JDBC
- Tomcat
Breaking parental delegation is too long to cover, and there will be a separate article to cover it. If you want to know the results, please refer to these two articles:
- How does JDBC break parental delegation
- Why does the Tomcat class loader violate the parental delegation model
Having said that, let’s take a look at what happens when a class is loaded:
- A complete class loading process goes through: load -> verify -> prepare -> parse -> initialize
- Load: read xx.class file from disk and generate its corresponding class object;
- Verify the format of the bytecode file (bytecode is machine code for the JVM system and has a certain format)
- Preparation: Allocates memory and assigns default values to static variables of the class. Boolean =false, int=0;
- Parsing: symbolic references are replaced with direct references (also known as static links). Symbolic references generated by methods and static variables are replaced with real reference addresses in memory during loading.
- Initialization: Initialize the class’s static variables, which we set ourselves, and execute the static code block;
conclusion
Thank you for reading my article patiently. If there are any mistakes in the article, please leave a comment and correct them. If there are any details in the article that are not clear enough, you can leave a comment in the comment section.
Do not do the best, but do better.