[深度理解java虚拟机]第7章 虚拟机类加载机制

个人博客地址

7.1 概述

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

7.2 类加载的时机

  • 类从被加载到虚拟机内存中开始。
    在这里插入图片描述
  • 虚拟机规定5种情况必须对类进行初始化(而加载、验证、准备、自然要在初始化之前完成):
    1. 遇到new、getstatic、putstatic、或invokestatic这4条字节码指令时,如果类没有进行初始化,则需要触发其初始化。(注意在设置一个类的静态字段不会触发初始化,原因是被final修饰的字段已经在编译器放入常量池中)。
    2. 使用java.lang.reflect包的方法对类进行反射调用的时候。
    3. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。但是初始化一个接口时,并不要求其父类接口全部都完成了初始化,只有真正使用到父接口的时候(如引用接口中定义的变量)才会初始化。
    4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

7.3 类加载的过程

7.3.1 加载

  • 过程:
    1. 通过一个类的全限定名来获取定义此类的二进制字节流。
    2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
  • 加载阶段与连接阶段的部分内容是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,

7.3.2 验证

  • 目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全。
  • 文件验证:验证字节流是否符合Class文件格式规范,并且能被当前版本的虚拟机处理。
  • 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求。
  • 字节码验证:主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:将符号引用转化为直接引用,这个转化动作将在连接的第三个阶段–解析阶段中发生。符号引用验证可以看作是对类自身以外的信息进行匹配校验。

7.3.3 准备

  • 正式为类变量分配内存并设置初始值的阶段,这些变量所使用的内存都是在方法区中分配。
  • 注意这个初始值是该类型初始值而不是程序指定的值,如int是0,char是’0’,引用类型是null。

7.3.4 解析

  • 是虚拟机将常量池内的符号引用替换为直接引用的过程。
  • 解析动作主要针对类或者接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

7.3.5 初始化

  • 通过程序制定的主观计划去初始化类变量和其他资源。或从另一个角度来表达:初始化阶段就是执行类构造器()方法的过程。
  • ()方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的,顺序是由语句在源文件中出现的顺序决定的,静态语句块只能访问到定义在静态语句块之前的变量,定义在之后的变量,在前面的静态语句块可以赋值,但是不能访问。
  • 虚拟机会保证在子类的()方法执行之前,父类的()方法已经执行完毕,因此在虚拟机中第一个被执行的()方法的类肯定是java.lang.Object。

7.4 类加载器

  • 把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到了java虚拟机外部去实现,以便让程序自己决定如何获取所需要的类。实现这个动作的代码模板称为“类加载器”。

7.4.1 类与类加载器

  • 比价两个类是否”相等“,只有在这两个类是同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那么这两个类就必定不相等。
  • 这里指的相等是包括类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果。

7.4.2 双亲委派模型

在这里插入图片描述

  • 启动类加载器:负责将存放在<JAVA_HOME>\lib目录中的类库加载到虚拟机内存中。
  • 扩展类加载器:负责加载<JAVA_HOME>\lib\ext目录中的所有类库。
  • 应用程序类加载器:负责加载用户类路径(ClassPath)上所指定的类库,一般情况这个就是程序的默认类加载器。
  • 双亲委派模型的工作过程:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的请求最终都应该传到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
发布了84 篇原创文章 · 获赞 50 · 访问量 7010

猜你喜欢

转载自blog.csdn.net/qq_43115606/article/details/104084372