目录
四、类加载过程
1.类加载过程
2.双亲委派模型(经典面试题)
什么是双亲委派模型
双亲委派模型的优点
破坏双亲委派模型
四、类加载过程
1.类加载过程
咱们写的java代码,是.java文件(硬盘),一个java进程要跑起来,就需要把.java先变成.class文件(硬盘),加载到内存中,得到“类对象”,其中就执行了指令,要执行的CPU指令,都是通过字节码(进入内存中)让JVM翻译出来的
从上⾯的图⽚我们可以看出整个JVM执⾏的流程中,和程序员关系最密切的就是类加载的过程了,所以接下来我们来看下类加载的执⾏流程。
对于⼀个类来说,它的⽣命周期是这样的:
类加载的几个环节(八股)
1.加载:在硬盘上,找到对应的.class文件,读取文件内容
2.验证:检查.class里的内容,是否符合要求
3.准备:给类对象,分配内存空间(元数据区)(类加载最终要得到的就是类对象),会把这个空间里的数据先全都填充为0(此时,如果这个类有静态成员,值就是0)
4.解析:针对字符串常量来初始化,把刚才.class文件中的常量的内容取出来,放到“元数据区”
5.初始化:针对类对象进行初始化(不是针对对象初始化,和构造方法无关),给静态成员进行初始化,执行静态代码块
此时类对象就搞定了
后续代码就可以使用这个类对象,创建实例,或者使用里面的静态成员了
2.双亲委派模型(经典面试题)
提到类加载机制,不得不提的⼀个概念就是“双亲委派模型”
站在Java虚拟机的⻆度来看,只存在两种不同的类加载器:⼀种是启动类加载器(Bootstrap ClassLoader),这个类加载器使⽤C++语⾔实现,是虚拟机⾃⾝的⼀部分;另外⼀种就是其他所有的类加载器,这些类加载器都由Java语⾔实现,独⽴存在于虚拟机外部,并且全都继承⾃抽象类java.lang.ClassLoader。
站在Java开发⼈员的⻆度来看,类加载器就应当划分得更细致⼀些。⾃JDK 1.2以来,Java⼀直保持着三层类加载器、双亲委派的类加载架构器
什么是双亲委派模型
它出现在“加载”环节(第一步),根据代码中的写的“全限定类名”(包名+类名)找到对应的.class文件
这个模型描述了JVM加载.class文件过程中,找文件的过程
“类加载模型”在JVM中包含的一个特定的模块/类,这个类负责完成后续的类加载工作
JVM中内置了三个类加载器,负责加载不同的类
1)BootstrapClaaLoader (相当于爷爷)
负责加载标准库的类
2)ExtentionClassLoader(相当于父亲)
负责加载JVM扩展库的类
3)ApplicationClassLoader(相当于儿子)
负责加载第三方库的类 和 自己写的代码的类
此处的“父子关系”不是通过类的继承表示的(不是父类子类),而是通过类加载器中存在一个“parent”这样的字段,指向自己的父亲,类似于二叉树中的“三叉实现形式”)
“双亲委派模型”本身翻译是不准确的,更准确的翻译:“父亲委派模型”,“单亲委派模型”
工作过程举例:
给定一个类的全限定类名:java111.Test
此时加载过程如下:
(1)工作从ApplicationClassLoader开始进行
ApplicationClassLoader并不会立即搜索第三方库的相关目录,而是把任务交给自己的父亲来进行处理
(2)工作就到ExtentionClassLoader
ExtentionClassLoader也不会立即搜索负责的扩展库的目录,也是把任务交给自己的父亲来处理
(3)工作就来到BootstrapClaaLoader
BootstrapClaaLoader也像交给自己的父亲来处理,但是它的parent指向null,只能自己处理,BootstrapClaaLoader尝试在标准库的路径中搜索上述的类
(4)工作回到了ExtentionClassLoader
此时就要搜索扩展库对应的目录了
如果找到了,就由当前的类加载器负责打开文件,读取文件等后续操作...
如果找不到,任务就还是要继续还给儿子来处理
(5)工作回到了ApplicationClassLoader
此时要搜索第三方库/用户项目代码的目录了
如果找到了,也就是由当前的类加载器负责处理
如果没有找到,任务还是要继续还给儿子来处理
but,此时没有儿子了,还没找到,最早就会抛出一个ClassNotFoundException
本质就是:拿到任务,先交给父亲处理,父亲处理不了,在自己处理
双亲委派模型的优点
1. 避免重复加载类:⽐如A类和B类都有⼀个⽗类C类,那么当A启动时就会将C类加载起来,那么在B类进⾏加载时就不需要在重复加载C类了。
2. 安全性:使⽤双亲委派模型也可以保证了Java的核⼼API不被篡改,如果没有使⽤双亲委派模型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个称为java.lang.Object类的话,那么程序运⾏的时候,系统就会出现多个不同的Object类,⽽有些Object类⼜是⽤⼾⾃⼰提供的因此安全性就不能得到保证了(JVM会确保加载的类是标准库的类)
破坏双亲委派模型
but 双亲委派模型虽然有其优点,但在某些情况下也存在⼀定的问题,⽐如Java中SPI(Service Provider Interface,服务提供接⼝)机制中的JDBC实现:
因为DriverManager位于rt.jar包,由BootStrap类加载器加载,⽽其Driver接⼝的实现类是位于服务商提供的Jar包中,是由⼦类加载器(线程上下⽂加载器 Thread.currentThread().getContextClassLoader)来加载的,这样就破坏了双亲委派模型了(双亲委派模型讲的是所有类都应该交给⽗类来加载,但JDBC显然并不能这样实现)