【学习笔记】—JVM(四)虚拟机类加载机制

一、什么是类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行检验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

二、类的生命周期

在这里插入图片描述
  加载、验证、准备、初始化和卸载这5个顺序是固定的,按这样的顺序开始,但是解析不一定,为了支持动态绑定的缘故

触发初始化的有且只有的5种场景(主动引用)

    1. 遇到new,getstatic,putstatic,invokstatic,指令时,若这个类没有初始化则进行初始化。
    2. 使用java.lang.reflect包的方法进行反射时。
    3. 初始化一个类时,其父类还没有初始化过。对其父类初始化。
    4. 指定一个要执行的主类(包含main()方法的类)时。
    5.使用动态语言支持时,最后解析的结果是REF_getstatic,REF_putstatic,REF_invokstatic。

  其他的引用都称之为被动引用,例如,子类引用父类的静态字段,子类不会初始化。引用定义的常量,因为常量在编译阶段会存入调用类的常量池,引用后没有直接引用到定义的常量,故不会初始化。

  接口和类差不多,但是类初始化时要求其父类全部初始化,而接口则是在真正用到父类接口时再初始化

三、类加载过程

加载

  非数组类的加载既可以使用系统提供的引导类加载器完成,也可以用户自定义。而数组类是由Java虚拟机直接创建,但是元素类型需要加载器创建。

  加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需要的格式存储在方法区之中。
  加载和连接阶段是交替进行的

验证

  为了确保Class文件得字节流中包含的信息符合当前虚拟机的要求。大致分为:文件格式验证,元数据验证,字节码验证,符号引用验证

  文件格式验证:检验魔数,版本号等等, 主要目的是摆正字节流能正确的解析并存储与方法区之内。这个阶段是基于二进制字节流进行的。后面的3个阶段都是基于方法区的存储结构来进行的,不会再直接操作字节流
  元数据验证:对信息进行语义分析是否符合Java语言规范。
  字节码验证:通过数据流和控制流分析,确定程序语义是合法的,符合逻辑的。但一个方法通过了字节码验证,也不能说他一定就是安全的
  符号引用验证:发生在虚拟机将符号引用转化为直接引用的时候。这个阶段非常重要但不必要,对那些反复使用和验证过的代码,可以设置不验证来缩短类加载的时间

准备

  正式为类变量分配内存并设置初值

解析

  将常量池内的符号引用替换为直接应用的过程。主要分为类或接口的解析,字段解析,类方法解析,接口方法解析

初始化

  在初始化阶段,可根据程序员通过程序制定的主观计划去初始化类变量和其他资源,也可说初始化阶段是执行类构造器<clinet>()方法

虚拟机会保证子类的<clinet>()方法执行之前,父类的<clinet>()方法已经执行完毕,因此虚拟机中第一个被执行的<clinet>()方法的类肯定是Object,且意味着父类中定义的静态语句块要优先于子类的变量赋值操作

  静态语句块中只能访问到定义在静态语句块之前的变量,定义在他之后的变量,在他前面的静态语句块中可以赋值,但不能访问

pubic class Test{
	static {
		i = 0;//可以赋值
		System.out.print(i);//报错,非法向前引用
	}
	static int i =1;
}

  如果多个线程同时去初始化一个类,那么只会有一个线程去执行<clinet>()其他的都要阻塞等待,显然会引起多个进程阻塞。

类加载器

通过一个类的全限定名来获取此类的二进制字节流,实现这个动作的的代码模块叫做类加载器。

  只用于实现类的加载动作。
  对于任何一个类都需要由加载他的类加载器和本身确立其唯一性。两个类来源于同一个Class文件,被同一个虚拟机加载只要他们的类加载器不同,那么他们绝对就不相等

四、双亲委派模型

为什么要双亲委派模型

  上面我们说到类相不相等的问题,为了保证相同的class文件,在使用的时候,是相同的对象
  工作过程

如果一个类加载器收到了加载某个类的请求,则该类加载器并不会去加载该类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是如此,因此所有的类加载请求最终都会传送到顶端的启动类加载器;只有当父类加载器在其搜索范围内无法找到所需的类,并将该结果反馈给子类加载器,子类加载器会尝试去自己加载。

从虚拟机角度来说只有两种类加载器,启动类加载器,是虚拟机自身的一部分。另一种就是所有其他类加载器,独立于虚拟机外部。
从开发人员的角度来看则是分为
  启动类加载器: 管理最核心的类例如:String。无法通过getClassLoader()获得ClassLoader名为null
  扩展类加载器
  应用程序加载器(默认加载器)
在这里插入图片描述
  Java类随着类加载器一起具备了带有优先级的层次关系(虽然叫父加载器,引用指向,但非继承关系,能通过getParent()获取引用。)。例如Oject不同的类加载器要加载这个类是,都要找到最顶端的启动类加载器。所以始终都是同一个Object,而如果然每个类加载器自己加载Object的话就会产生很多个Oject,因为类加载不同他们不能相等不能算作一个,就会产生混乱。

破坏双亲委派模型

  在某些情况下父类加载器需要委托子类加载器去加载class文件。更具前面的双亲工作过程,父类加载器无法加载到需要的文件,所以要破坏它

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值