JVM类加载机制

概述

  • 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
  • 在Java里,类型的加载、连接和初始化都是在程序运行期间完成的。
  • 类加载的整个生命周期有七个阶段:加载、验证、准备、解析、初始化、使用、卸载,其中验证、准备、解析被称为连接。
  • 解析阶段不一定按顺序执行,在某些情况下可以在初始化阶段之后执行,这是为了支持Java语言的运行时的绑定(动态绑定)。

加载过程

1. 加载

  • 在此阶段,虚拟机需要完成3件事情。

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

2. 验证

  • 目的:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机本身。

  • 动作

    1. 文件格式验证
      • 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
      • 本阶段验证是基于二进制字节流进行的,只有通过了这个阶段的验证后,字节流才会进入内存的方法区中进行存储,所以后面的3个验证阶段全部都是基于方法区的存储结构进行的,不会再直接操作字节流。
    2. 元数据验证
      • 对字节码描述的信息进行语义分析。
      • 主要目的:对类的元数据信息进行语义校验。
    3. 字节码验证
      • 通过对数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
    4. 符号引用验证
      • 确保解析动作能正常执行。

3. 准备

  • 为类变量分配内存并设置类变量的初始值,这些变量所使用的内存都将在方法区中进行分配。
  • 这个时候进行内存分配的对象仅包括类变量(被static修饰的变量),而不包括实例变量。

4. 解析

  • 将常量池内的符号引用替换为直接引用的过程。
  • 过程
    1. 类或接口的解析
    2. 字段解析
    3. 类方法解析
    4. 接口方法的解析

5. 初始化

  • 在初始化阶段,根据程序员通过程序制定的主观计划去初始化变量和其他资源。
  • 静态语句块只能访问定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。

类加载器

  • 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。
  • 比较两个类是否"相等",只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,则这两个类就必定不相等。

双亲委派模型

  • 从虚拟机的角度将,只有两种不同的类加载器:

    1. 启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++实现,是虚拟机自身的一部分。
    2. 其他所有类加载器,这些类加载器都是由Java实现,独立于虚拟机外部,并且全都继承自抽象类Java.lang.ClassLoader。
  • 类加载器:

    1. 启动类加载器(Bootstrap ClassLoader):负责将JAVA_HOME\lib目录中或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存。
    2. 扩展类加载器(Extension ClassLoader):负责加载JAVA_HOME\lib\ext目录中或者被java.ext.dirs系统变量所指定的路径中的所以类库,开发者可以直接使用扩展类加载器。
    3. 应用程序加载器(Application ClassLoader):负责ClassPath上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
  • 下图中类加载器之间的这种层次关系,称为类加载器的双亲委派模型

  • 双亲委派模型要求除了启动类加载器外,其余的类加载器都应当有自己的父加载器,这里的父子关系一般不是用继承,都是使用组合来服用类加载器的代码。
  • 工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
  • 好处:Java类随着它的类加载器一起具备了一种带有优先级的层次关系。
    例如java.lang.Object类,它存放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因为Object类在程序的各种类加载器环境中都是同一个类。
  • 作用:保证Java程序的稳定运行。

posted on 2017-02-27 20:13  liuyang0  阅读(466)  评论(0编辑  收藏  举报

导航

Flag Counter