Java类加载机制--类加载过程(加载)

Java虚拟机加载类的全过程包括:加载、验证、准备、解析、初始化。验证、准备、解析叫连接过程。今天我们讲加载。
首先明确“加载”是“类加载”的一个过程,不要混淆。加载过程中,虚拟机需要完成下列三个事情:

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

虚拟机的这三点要求不算具体,因此虚拟机实现与具体应用的灵活度都是相当大的。就拿第一条说,他仅仅要求“通过一个类的全限定名获取定义此类的二进制字节流”,并没有指明二进制字节流要从Class文件获取,也没说要怎样获取,从哪里获取。所以说java虚拟机的设计团队在加载阶段搭建了一个很广阔的舞台,许多举足轻重的java技术都建立在这个舞台上,比如:

  • 从ZIP包中获取,这是jar,war格式的基础
  • 从网络中获取,这种场景的典型应用就是Applet
  • 运行时计算生成,这种场景使用的最多的就是动态代理技术,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass来为特定接口生成形式为“*$Proxy”的代理类的二进制字节流
  • 由其他文件生成,典型的场景是JSP应用,即JSP文件生成对应的Class类
  • 从数据库中读取,这种场景相对少见

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

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

  • 如果数组的组件类型(指的是数组去掉一个维度的类型)是引用类型,那就递归采用本节中定义的加载过程去加载这个组件类型,数组C将在加载该类型组件的类加载器的类名称空间上被标识(这点很重要,一个类必须与类加载器一起确定唯一性)
  • 如果数组的组件类型不是引用类型(例如int[]数组),Java虚拟机将会把数组C标记为与引导类加载器关联
  • 数组类的可见性与他的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为public

加载阶段完成之后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,方法区中的数据存储格式由虚拟机实现自行定义,虚拟机规范未规定此区域的具体数据结构。然后在内存中实例化一个java.lang.Class类的对象(并没有明确规定是在java堆中,对于HotSpot虚拟机而言,Class对象比较特殊,他虽然是对象,但是放在方法区中),这个对象将作为程序访问方法区中的这些类型数据的外部接口。
加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能开始了,但是这些夹在加载阶段之中进行的动作,任然属于连接阶段的内容,这两个阶段的开始时间任然保持着固定的先后顺序。

猜你喜欢

转载自blog.csdn.net/huqianlei/article/details/90734570