类加载-类的声明周期

Posted 517cn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载-类的声明周期相关的知识,希望对你有一定的参考价值。

  类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个部分统称为连接(Linking),这7个阶段的发生顺序如下图所示。
技术图片

  加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。注意,这里笔者写的是按部就班地“开始”,而不是按部就班地“进行”或“完成”,强调这点是因为这些阶段通常都是互相交叉地混合式进行的,通常会在一个阶段执行的过程中调用、激活另外一个阶段。 

 

加载

  “加载”是“类加载”(Class Loading)过程的一个阶段,希望读者没有混淆这两个看起来很相似的名词。在加载阶段,虚拟机需要完成以下3 件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java. lang. Class 对象,作为方法区这个类的各种数据的访问入口。

  虚拟机规范的这3 点要求其实并不算具体,因此虚拟机实现与具体应用的灵活度都是相当大的。例如“通过一个类的全限定名来获取定义此类的二进制字节流”这条,它没有指明二进制字节流要从一个Class 文件中获取,准确地说是根本没有指明要从哪里获取、怎样获取。虚拟机设计团队在加载阶段搭建了一个相当开放的、广阔的“舞台”,Java 发展历程中,充满创造力的开发人员则在这个“舞台”上玩出了各种花样,许多举足轻重的Java 技术都建立在这一基础之上,例如:

  •   从ZIP 包中读取,这很常见,最终成为日后JAR、EAR、WAR 格式的基础。
  •   从网络中获取,这种场景最典型的应用就是Applet。
  •   运行时计算生成,这种场景使用得最多的就是动态代理技术,在java. lang. reflect. Proxy 中,就是用了ProxyGenerator. generateProxyClass 来为特定接口生成形式为"*$ Proxy" 的代理类的二进制字节流。
  •   由其他文件生成,典型场景是JSP 应用,即由JSP 文件生成对应的Class 类。
  •   从数据库中读取,这种场景相对少见些,例如有些中间件服务器(如SAP Netweaver)可以选择把程序安装到数据库中来完成程序代码在集群间的分发。

  相对于类加载过程的其他阶段,一个非数组类的加载阶段(准确地说,是加载阶段中获取类的二进制字节流的动作)是开发人员可控性最强的,因为加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器去完成,开发人员可以通过定义自己的类加载器去控制字节流的获取方式(即重写一个类加载器的loadClass() 方法)。

  对于数组类而言,情况就有所不同,数组类本身不通过类加载器创建,它是由Java 虚拟机直接创建的。但数组类与类加载器仍然有很密切的关系,因为数组类的元素类型(Element Type,指的是数组去掉所有维度的类型)最终是要靠类加载器去创建,一个数组类(下面简称为C)创建过程就遵循以下规则:

  如果数组的组件类型(Component Type,指的是数组去掉一个维度的类型)是引用类型,那就递归采用本节中定义的加载过程去加载这个组件类型,数组C 将在加载该组件类型的类加载器的类名称空间上被标识(这点很重要,在7. 4 节会介绍到,一个类必须与类加载器一起确定唯一性)。

  如果数组的组件类型不是引用类型(例如int[] 数组),Java 虚拟机将会把数组C 标记为与引导类加载器关联。

  数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为public。

  加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在法区之中,方法区中的数据存储格式由虚拟机实现自行定义,虚拟机规范未规定此区域的具体数据结构。然后在内存中实例化一个java.lang.Class类的对象(并没有明确规定是在Java堆中,对于HotSpot虚拟机而言,Class对象比较特殊,它虽然是对象,但是存放在方法区里面),这个对象将作为程序访问方法区中的这些类型数据的外部接口。

  加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于连接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。

以上是关于类加载-类的声明周期的主要内容,如果未能解决你的问题,请参考以下文章

类的生命周期

JVM类加载器及Java类的生命周期

一文读懂Java类加载机制

JVM类生命周期概述:加载时机与加载过程

类的生命周期

深入理解Java虚拟机——类加载机制