【Java虚拟机学习】-类加载机制

概述

  • 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
  • 在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成,这虽然增量一些性能开销,但是会为Java应用程序提供高度的灵活性。

类加载的时机

  • 类的整个生命周期:加载、验证、准备、解析、初始化、使用和卸载;其中验证、准备和解析统称为连接;
  • 虚拟机规范没有强制约束类加载的时机,但严格规定了有且只有5种情况必须立即对类进行初始化:遇到new、getstatic、putstatic和invokestatic指令;对类进行反射调用时如果类没有进行过初始化;初始化时发现父类还没有进行初始化;虚拟机启动指定的主类;动态语言中MethodHandle实例最后解析结果REF_getStatic等的方法句柄对应的类没有初始化时;

类加载的过程

加载
  • 通过一个类的全限定名来获取定义此类的二进制字节流;
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;
验证
  • 验证是连接阶段的第一步,其目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全;
  • 验证阶段是非常重要的,这个阶段是否严谨决定了Java虚拟机是否能承受恶意代码的攻击;
  • 校验动作:文件格式验证(基于二进制字节流)、元数据验证(对类的元数据语义分析)、字节码验证(对方法体语义分析)、符号引用验证(对类自身以外的信息进行匹配性校验);
准备
  • 正式为变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在这个方法区中进行分配;
  • 需要强调两点:这时候内存分配的仅包括类变量,而不包括类实例变量;这里所说的初始化通常情况下是数据类型的零值,真正的赋值是在初始化阶段,如果是static final的则是直接赋值;
解析
  • 解析阶段是虚拟机将常量池内的符号引用(如CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等7种)替换为直接引用的过程;
  • 符号引用可以是任何形式的字面量,与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中;而直接引用是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄,它和虚拟机实现的内存布局相关,引用的目标必定以及在内存中存在;
  • 对同一个符号引用进行多次解析请求是很常见的事情,虚拟机实现可以对第一次解析的结果进行缓存;
初始化
  • 是类加载过程的最后一步,真正开始执行类中定义的Java程序代码(或者说是字节码);
  • 初始化阶段是执行类构造器方法的过程,该方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的;
  • 方法与类的构造函数(或者说是实例构造器方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的方法执行之前,父类的方法已执行完毕;
  • 执行接口的方法不需要先执行父接口的方法,只有当父接口中定义的变量使用时父接口才会初始化,接口的实现类在初始化时也一样不会执行接口的方法;
  • 方法初始化是加锁阻塞等待的,应当避免在方法中有耗时很长的操作;

类加载器

  • 虚拟机设计团队把类加载阶段的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到虚拟机外部去实现,实现这个动作的代码模块称为类加载器;
  • 这时Java语言的一项创新,也是Java语言流行的重要原因,在类层次划分、OSGI、热部署、代码加密等领域大放异彩;
类与类加载器
  • 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机的唯一性,每一个类加载器都拥有一个独立的类名称空间;
  • 比较两个类是否相等(如Class对象的equals方法、isAssignableFrom方法、isInstance方法),只有在这两个类是由同一个类加载器加载的前提下才有意义;
双亲委派模型

双亲委派模型

  • 三种系统提供的类加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载器(Application ClassLoader);
  • 双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器,这里一般不会以继承的关系来实现,而是使用组合的关系来复用父加载器的代码;
  • 其工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,只有父类加载器反馈自己无法完成这个加载请求时(它的搜索范围中没有找到所需的类),子加载器才会尝试自己去加载;
  • 这样的好处是Java类随着它的类加载器具备了一种带有优先级的层次关系,对保证Java程序的稳定运作很重要;
  • 实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass方法中,逻辑清晰易懂;
破坏双亲委派模型
  • 上一小节的双亲委派模型是Java设计者推荐给开发者的类加载器实现方法,但不是一个强制性的约束模型;
  • 典型的两种情况:为了解决JNI接口提供者(SPI)引入的线程上下文类加载器;为了程序动态性加强的OSGI的Bundle类加载器;

本章小结

本章介绍了类加载过程的加载、验证、准备、解析和初始化五个阶段中虚拟机进行了哪些动作,还介绍了类加载器的工作原理及其对虚拟机的意义。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值