第7章 虚拟机类加载机制

第7章 虚拟机类加载机制

  • Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。
  • 与那些在编译时需要进行连接的语言不同,在Java语言里面,类型的加载,连接和初始化过程都是在程序运行期间完成的。

7.2 类加载的时机

  • 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载,验证,准备,解析,初始化,使用和卸载七个阶段,其中验证,准备,解析三个部分统称为连接。
  • 第一个阶段“加载”可以交给虚拟机的具体实现来自由把握,但是对于初始化阶段,Java虚拟机规范则是严格规定了有且只有六种情况必须立即对类进行“初始化”(而加载,验证,准备自然需要在此之前开始)
  • 这六种情况的行为称为对一个类型进行主动引用。除此之外,所有引用类型方式都不会触发初始化,称为被动引用。
  • Java语言中对数组的访问要比C/C++相对安全,很大程度上是因为这个类包装了数组元素的访问,而C/C++中则是直接翻译为对数组指针的移动,在Java语言里,当检查到数组越界会抛出java.lang.ArrayIndexOutOfBoundException异常,避免了直接造成非法内存访问。
  • 接口的加载过程与类加载过程稍有不同;接口也有初始化过程,这点与类是一致的,上面的代码都是用静态语句块“static{}”来输出初始化信息的,而接口中不能使用“static{}”语句块,但编译器仍然会为接口生成“()”类构造器,用于初始化接口中定义的成员变量。
  • 一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。

7.3 类加载过程

7.3.1 加载
  • 类加载的全过程是加载,验证,准备,解析和初始化五个阶段。注意区分加载和类加载。加载只是类加载的一个阶段。
  • 相对于类加载过程的其他阶段,非数组类型的加载阶段(准确地说,是加载阶段中获取类的二进制字节流的动作)是开发人员可控性最强的阶段。
  • 对于数组类而言,情况就有所不同,数组类本身不通过类加载器创建,它是由Java虚拟机直接在内存中动态构造出来的。
  • 类型数据妥善安置在方法区后,会在Java堆内存中实例化一个java.lang.Class类的对象,这个对象将作为程序访问方法区中的类型数据的外部接口
7.3.2 验证
  • 验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合Java虚拟机规范的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。
  • 从整体上看,验证阶段大致上会完成四个阶段的检验动作:文件格式验证,元数据验证,字节码验证和符号引用验证
  • 符号引用验证的主要目的是确保解析行为能正常执行。
7.3.3 准备
  • 准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。
  • 方法区本身是一个逻辑上的区域,在JDK7及之前,HotSpot使用永久代来实现方法区时,实现是完全符合这种逻辑概念的;而在JDK8及以后,类变量则会随着Class对象一起存放在Java堆中,这时候“类变量在方法区”就完全是一种对逻辑概念的表述了。
7.3.4 解析
  • 解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。
  • 符号引用以一组符号来秒速所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
  • 直接引用是可以直接指向目标的指针,相对偏移量或者是一个能间接定位到目标的句柄。
7.3.5 初始化
  • 类的初始化阶段是类加载过程中的最后一个步骤。直到初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码,将主导权交给应用程序。
  • 初始化阶段就是执行类构造器()方法的过程。()并不是程序员在java代码中直接编写的方法,它是Javac编译器的自动生成物。
  • 接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成方法。但接口与类不同的是,执行接口的()方法不需要先执行父接口的方法,因为只有当父接口中定义的变量被使用时,父接口才会被初始化。此外,接口的实现类在初始化时也一样不会执行接口的()方法。

7.4 类加载器

  • Java虚拟机团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)
7.4.1 类与类加载器
7.4.2 双亲委派模型
  • 站在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另外一种就是其他所有类的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部。
  • 绝大多数Java程序都会使用到以下3个系统提供的类加载器来进行加载
    • 启动类加载器
    • 扩展类加载器
    • 应用程序类加载器
  • 各种类加载器之间的关系被称为类加载器的“双亲委派模型”,双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里的类加载器之间的关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。
  • 使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。

关于类加载器以及线程上下文加载器相关问题
或者是 https://techlog.cn/article/list/10183177

Classloader 类结构分析
jvm中的ClassLoader()中的resolveClass方法的作用

7.5 Java模块化系统

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值