类加载器和类加载过程
本篇主要内容:
- JVM工做结构简图。
- 类加载过程。
- 类加载器(ClassLoader)。
- 自定义类加载器实现步骤。
JVM工做结构简图

类加载过程
上图中咱们看到了字节码文件通过类加载子系统加载到JVM中,那么如今咱们在看类加载子系统中发生了什么。
先放一张类加载过程的简图。

其中包括了三个大步骤,他们分别是:java
加载
加载指的是将类的class文件读入到内存,并为之建立一个java.lang.Class对象。安全
- 经过类的全限定名获取此类的二进制字节流。
- 将这个字节流所表明的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个Class对象,做为方法区这个类的各类数据的访问入口。
加载一般由类加载器完成,加载类的方式具体有如下几项:网络
- 本地资源加载。
- 网络加载。Web Applet。
- zip压缩包加载。jar,war。
- 运行时计算生成。动态代理技术。
- 其余文件生成。JSP应用。
- 从加密文件中读取,主要是为了防止class文件被反编译。
连接
连接分为三个步骤:数据结构
- 验证
确保class文件的字节流中包含信息符合虚拟机要求。
主要包括,文件格式验证,元数据验证,字节码验证,符号引用验证。
- 准备
为类变量分配内存,并为变量赋零值。
这里不包括用final修饰的static,由于final在变异的时候就会分配了,准备阶段会显式初始化。
不会为实例变量分配初始化。
- 解析
将常量池内的符号引用变为直接引用。
初始化
- 初始化阶段就是执行类构造器方法<clinit>()方法。
- <clinit>()方法是由javac将类变量赋值动做和静态代码块中的语句合并自动产生的。当类不存在类变量和静态代码块时不会自动生成此方法。
- 一个类只会被加载一次,<clinint>()方法是同步加锁的。(后付测试代码)
注:若是想看此方法,能够使用JClassLib软件或IDEA中的JClassLib插件。
附一张clinit<>()方法图和同步测试代码。
测试
package cn.lele;
public class ClinitTest02 {
public static void main(String[] args) {
Runnable r = ()->{
System.out.println(Thread.currentThread().getName() + "start");
DeadThread deadThread = new DeadThread();
System.out.println(Thread.currentThread().getName() + "finish");
};
Thread a = new Thread(r,"A");
Thread b = new Thread(r,"B");
a.start();
b.start();
}
}
class DeadThread {
static {
if (true) {
System.out.println(Thread.currentThread().getName() + "初始化当前类");
while (true) {
}
}
}
}
图中咱们看到在number声明以前咱们就能够在同步代码块中为他赋值,这是由于在连接的准备阶段,number变量已经分配了内存空间而且赋予了零值。加密
类加载器(ClassLoader)
类加载过程的第一步加载,是须要类加载器来完成的,Java为咱们提供了三个类加载器来完成这一步。spa
引导类加载器
- 该引导器使用C/C++实现。
- 该引导器负责加载JAVA的核心类库。
- 加载扩展类加载器和应用程序加载器,并制定为他们的父加载器。
- 出于安全考虑,只会加载包名为java,javax,sun等开头的类。
扩展类加载器
- sun.misc.Launcher$ExtClassLoader实现
- 从JDK安装目录的jre/lib/ext子目录下加载类库。
- 父类加载器为null,由于正常状况下咱们是获取不到引导类加载器的。
应用程序类加载器
- 也是sun.misc.Launcher类中经过内部类的方式实现。
- 负责classpath指定路径下的类加载,默认使用此加载器。
注:
1.JVM规范中提到,JVM只支持引导类加载器和自定义类加载器,规范将全部派生于抽象类ClassLoader的类加载器划分为自定义类加载器。
2.引导类加载器 -> 扩展类加载器 -> 应用程序类加载器为上下级关系,并不是继承关系。
自定义类加载器
为何要自定义类加载器。
- 隔离加载类。
- 修改类加载的方式。
- 扩展加载源。
- 防止源码泄露。
怎么自定义类加载器。
- 继承ClassLoader类,将自定义的逻辑写在findClass()方法中。
- 若是没有特别复杂的需求,可直接继承URLClassLoader类。
文笔有限,如有错误,还望指正,感激涕零。