JVM类加载机制_双亲委派模型
【摘要】 类加载过程我们刚刚了解到我们的JVM执行流程,就是将一个.class文件加载到内存中,然后根据.class文件构造一个类对象,当类对象结束使用后,一个类的生命周期也就结束!而我们的类加载过程一共分为3个步骤!加载(Loading)加载过程主要做的,就是先找到对应的.class文件,然后打开并读取.class文件,同时初步生成一个类对象!Loading阶段最关键就是找到对应的.class文件...
类加载过程
我们刚刚了解到我们的
JVM
执行流程,就是将一个.class
文件加载到内存中,然后根据.class
文件构造一个类对象,当类对象结束使用后,一个类的生命周期也就结束!
而我们的类加载过程一共分为3个步骤!
- 加载(Loading)
加载过程主要做的,就是先找到对应的
.class
文件,然后打开并读取.class
文件,同时初步生成一个类对象!
Loading
阶段最关键就是找到对应的.class
文件,并且将其解析成类对象,那么如何去找到一个.class
文件呢?
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
我们的JVM
通过上述格式,就可以将一个.class
文件找到,然后通过这个格式进行加载初步构造一个类对象!
-
连接(Linking)
连接又分为3个步骤:- 验证(Verification)
这里验证就是通过
.class
文件中的内容对照JVM中提供的class文件标准格式信息进行对照,如果不符合格式,就会类加载失败,然后抛出异常- 准备(Preparation)
准备阶段就是给静态变量(类变量)分配内存,并且统一初始化置为0
- 解析(Resolution)
解析阶段是JVM将常量池内的符号引用替换成直接引用的过程,也就是初始化常量的过程
我们的.class
文件中的常量是集中放置的.然后每个常量都会对应一个标号引用,而我们的class文件结构体初始只是记录了标号引用,所以我们要根据标号引用拿到对应常量,对常量进行初始化,填充到类对象中! -
初始化(Initialization)
这里才是真正对类进行初始化,尤其是静态变量!
初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器方法的过程。
经典面试代码题
class A {
public A(){
System.out.println("A构造方法");
}
{
System.out.println("A构造代码块");
}
static {
System.out.println("A静态代码块");
}
}
class B extends A{
public B(){
System.out.println("B构造方法");
}
{
System.out.println("B构造代码块");
}
static {
System.out.println("B静态代码块");
}
}
public class Main extends B{
public static void main(String[] args) {
new B();
new B();
}
}
输出上述代码的打印结果:
上述类型题目抓住3个要点即可攻破!
- 类加载要优先加载类属性(所以这里的静态代码块优先加载),而创建实例之前需要执行类加载过程,并且类加载自进行一次!
- 构造方法在构造代码块前,并且每次实例化都会调用对应的构造方法!
- 父类执行完再子类!
双亲委派模型
提到类加载机制,不得不提的一个概念就是“双亲委派模型”!
我们通过对类加载机制的学习了解了类的一个生命周期!
在第一个Loading加载环节!我们需要通过类名去找到对应的类,而双亲委派模型就是JVM使用的找到一个类的机制!
我们的JVM
对类加载目录扫描进行了分工!
通过不同的加载器扫描不同的目录,主要有3个类加载器!
- 启动类加载器(Bootstrap ClassLoader)
类启动加载器,就是负责加载JDK中标准库中的类(scanner,String,ArrayList…)
- 扩展类加载器(Extension ClassLoader)
负责加载JDK中扩展的类!
- 应用程序类加载器(Application ClassLoader)
应用程序类加载器负责加载我们开发人员自己构造的类!
我们的JVM如何通过上述类加载器找到对应的类呢?
- 实例一:找到
java.lang.String
类!
首先第一步来到应用程序类加载器,然后应用程序类加载器会首先检查他的父类加载器是否加载过,如果没有就就调用父类,也就是扩展类加载器,扩展类加载器,也是先检查父类是否加载过,没有就调用父类加载器,启动类加载器,然后检查父类是否加载过,显然他没有父类,就对其下的目录进行扫描,然后在标准库目录找到了该类,也就加载结束!
实例二: 找到加载一个 Test
类
也是和实例一的步骤一样,逐层向上调用加载,到达启动类加载器后扫描后并没有找到,就再次回到扩展类加载器,扫描结束,回到程序类加载器,扫描后在其目录,将其类加载!
JVM为啥这样设计呢?
- 安全性: 我们通过这样父类优先类加载的过程,就保证了一个如果我们开发人员写的一个类名和标志库中类一样时,优先加载标志库中的类,也能顺利加载到标志库中的类!
- 避免重复加载类:如果A类和B类都有一个相同的父类C的话,当A启动后,就会将C类加载起来,那么B类启动会无需重复加载C类了!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)