🚀🚀🚀ClassLoader加载类的过程(图示)
🚀🚀🚀ClassLoader加载类的三个过程:
0. javac将java文件编译为class字节码文件
- 在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。
- 各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
1. (Loading)ClassLoader会读取class文件,将class文件读取进入内存生成Class类
- 1.1 通过一个类的全限定名来获取该类的二进制字节流。
- 1.2 将这个字节流的静态存储结构转化为方法区运行时数据结构。
- 1.3 根据运行时数据结构在内存堆中生成一个代表该类的java.lang.Class对象,作为该类数据的访问入口。
2. (Linking)ClassLoader对Class类进行检查
- 2.1 (验证)判断Class类是否符合JVM规范。
- 2.2 (准备)为类的类变量分配内存空间(此时会进行第一次初始化赋值,final、static)。
- 2.3 (解析)将符号引用替换为直接引用。
3. (Initialization)执行<clinit>方法,完成类变量的赋值
- 3.1 初始化阶段是执行类构造器<clinit>()方法的过程。
-
- 类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。
- 3.2 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先触发其父类的初始化。
- 3.3 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
<init> is the (or one of the) constructor(s) for the instance, and non-static field initialization.
<clinit> are the static initialization blocks for the class, and static field initialization.
上面这两句是Stack Overflow上的解释:
- init是instance实例构造器,对非静态变量解析初始化;
- clinit是class类构造器对静态变量,静态代码块进行初始化。
一、ClassLoader的分类
/**
* 观察自定义类创建时使用的类加载器ClassLoader
*/
public class Test {
public static void main(String[] args) {
Product product1 = new Product();
Class<? extends Product> aClass = product1.getClass();
System.out.println(aClass);
// sun.misc.Launcher$AppClassLoader@18b4aac2
// AppClassLoader 应用程序类加载器
ClassLoader classLoader = product1.getClass().getClassLoader();
System.out.println(classLoader);
// sun.misc.Launcher$ExtClassLoader@1b6d3586
// AppClassLoader的父类是ExtClassLoader
// ExtClassLoader和AppClassLoader都是Launcher的内部类
ClassLoader parent = classLoader.getParent();
System.out.println(parent);
String str = new String("hello");
System.out.println(str.getClass());
// null(Bootstrap ClassLoader)
// Java核心类库都是使用“引导类加载器”加载
System.out.println(str.getClass().getClassLoader());
}
}
二、Java核心类库有哪些?各个加载器主要加载哪些类?
* rt.jar
package xyz.xx.chapter2;
import sun.misc.Launcher;
import java.net.URL;
import java.util.Arrays;
/**
* F4 查看当前类的Hierarchy(等级图)
* Ctrl+Shift+T 搜索指定类
*/
public class ClassLosderTest1 {
public static void main(String[] args) {
// Bootstrp ClassLoader加载范围
// 1. jre/lib/目录下一些jar包
/*
file:/D:/Installation/jdk1.8.0_241/jre/lib/resources.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/rt.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/sunrsasign.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/jsse.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/jce.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/charsets.jar
file:/D:/Installation/jdk1.8.0_241/jre/lib/jfr.jar
file:/D:/Installation/jdk1.8.0_241/jre/classes
*/
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for(URL i : urLs){
System.out.println(i);
}
// extClassLoader加载范围
// 1. jre/lib/ext文件夹下jar包/class文件
// 2. 系统属性java.ext.dirs对应的jar包/class文件
/*
D:\Installation\jdk1.8.0_241\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
*/
String property1 = System.getProperty("java.ext.dirs");
for(String i : property1.split(";")){
System.out.println(i);
}
// AppClassLoader加载范围
// 1. java.class.path系统属性所指定的目录
// 2. 环境变量classpath中的jar包/class文件
/*
D:\Installation\jdk1.8.0_241\jre\lib\charsets.jar
D:\Installation\jdk1.8.0_241\jre\lib\deploy.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\access-bridge-64.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\cldrdata.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\dnsns.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\jaccess.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\jfxrt.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\localedata.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\nashorn.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\sunec.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\sunjce_provider.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\sunmscapi.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\sunpkcs11.jar
D:\Installation\jdk1.8.0_241\jre\lib\ext\zipfs.jar
D:\Installation\jdk1.8.0_241\jre\lib\javaws.jar
D:\Installation\jdk1.8.0_241\jre\lib\jce.jar
D:\Installation\jdk1.8.0_241\jre\lib\jfr.jar
D:\Installation\jdk1.8.0_241\jre\lib\jfxswt.jar
D:\Installation\jdk1.8.0_241\jre\lib\jsse.jar
D:\Installation\jdk1.8.0_241\jre\lib\management-agent.jar
D:\Installation\jdk1.8.0_241\jre\lib\plugin.jar
D:\Installation\jdk1.8.0_241\jre\lib\resources.jar
D:\Installation\jdk1.8.0_241\jre\lib\rt.jar
D:\IDEA_Projects\P001\out\production\P001
D:\Installation\ideaIC-2020.2.1.win\lib\idea_rt.jar
*/
String property = System.getProperty("java.class.path");
for(String i : property.split(";")){
System.out.println(i);
}
}
}
三、获取ClassLoader的几种方法
查看JVM系统属性清单:
for(String i : System.getProperties().toString().split(",")){ System.out.println(i); }
获取当前所在的类的类名:
System.getProperty("sun.java.command");
1> 通过Class类获取
package xyz.xx.chapter2;
/**
* F4 查看当前类的Hierarchy(等级图)
* Ctrl+Shift+T 搜索指定类
* Ctrl+Alt+o 删除不必要的包
*/
public class ClassLoaderTest2 {
// 测试获取ClassLoader的方法
public static void main(String[] args) throws ClassNotFoundException {
// 1.
// sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader = Class.forName(System.getProperty("sun.java.command")).getClassLoader();
System.out.println(classLoader);
}
}
2> 通过线程获取
package xyz.xx.chapter2;
/**
* F4 查看当前类的Hierarchy(等级图)
* Ctrl+Shift+T 搜索指定类
* Ctrl+Alt+o 删除不必要的包
*/
public class ClassLoaderTest2 {
// 测试获取ClassLoader的方法
public static void main(String[] args) throws ClassNotFoundException {
// 1.
// sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader = Class.forName(System.getProperty("sun.java.command")).getClassLoader();
System.out.println(classLoader);
// 2.
// sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader1);
}
}