继续jvm的学习,昨天学习了根据十六进制的class文件分析还原类结构,今天学习类加载的过程

一、类加载的过程

1、加载(类加载过程中的其中一个过程)

(1)通过一个类的全限定名来获取定义此类的二进制字节流

(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

(3)在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口

 

虚拟机规范的这三点并不具体,例如通过全限定名才获取字节流,并没有限制获取的方式,在此基础上,可以自定义类加载器,或者从jar、ear、war等格式读取,也可以运行时计算生成,典型的场景就是代理技术,或者还可以由其他文件生成,比如说jsp。

 

2、验证

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

 

大都会完成下面四个阶段的检验过程:文件格式验证、元数据验证、字节码验证、符号引用验证

 

(1)文件格式验证:验证字节流是否符合Class文件格式的规范,并且能够被当前版本的虚拟机处理。经过这个阶段的验证后,字节流才会进入内存的方法区中进行存储,所以后面的三个验证阶段全部是基于方法区的存储结构进行的。

(2)元数据验证:对字节码描述的信息进行语义分析,以保证符合java规范,例如检查是否继承了不允许继承的类

(3)字节码验证:进行数据流和控制流分析,对类的方法体进行校验分析,例如子类和父类的类型转换

(4)符号引用验证:对类自身以外的信息进行匹配性的校验,例如引用中的类、字段和方法的访问性是否可被当前类访问

 

3、准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。

首先,这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。其次是这里所说的初始值通常情况下是数据类型的零值。

 

4、解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

 

5、初始化

类初始化阶段是类加载过程的最后一步,开始执行类中定义的java程序代码

 

二、类加载器

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

1、类与类加载器

类加载器虽然只用于实现类的加载动作,但它在java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性。比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使他们来源于同一个class文件,只要他们的类加载器不同,那这两个类就必定不相等。

2、双亲委派模型

站在虚拟机的角度讲,只存在两种不同的类加载器:一种是启动类加载器,这个类加载器使用C++语言实现,是虚拟机滋生的一部分;另外一种就是所有其他的类加载器,这些类加载器都有java实现,独立于虚拟机之外,并且全部继承自抽象类java.lang.ClassLoader。

 

类加载器之间的关系是启动类加载器--扩展类加载器--应用程序类加载器

 

工作工程:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器,因此最终的加载请求都会委派到顶层的启动类加载器中,只有当父加载器反馈自己无法加载这个类时,子加载器才回去尝试自己加载。

 

双亲委派模型的好处是java类随着它的类加载器一起具备了一种带优先级的层次关系。例如java.lang.Object类,他存放在rt.jar中,无论哪个类加载器要加载这个类,最终都委派给启动类加载器加载,所以Object在程序各种类加载环境中都是同一个类,保证了java类型体系中最基础的行为。