JVM 类加载机制

一、类加载机制的描述

什么是虚拟机的类加载机制呢? 作为一个java程序员,应该知道我们写的java程序是要编译成class文件的,也就是说我们写的需要jvm执行的信息都存放在class文件中,那么jvm怎么获取class文件中的信息呢?虚拟机把class中的信息加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是JVM的类加载机制。

二、类加载的时机

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括加载、验证、准备、解析、初始化、使用、卸载7个阶段。其中验证、准备、解析部分统称为连接。这七个阶段可以用如下图描述:

类加载的七个阶段

从上图中可以明显看出各个阶段是有顺序的,加载、验证、准备、初始化这个5个阶段的顺序是固定的,也就是说类的加载过程必须按照这种顺序按部就班开始;解析阶段则不一定,解析阶段的工作完全可能在初始化之后才开始,之所以这么设计,就是为了支持Java语言的动态绑定。还有一点需要注意的是,虽然上述的5个阶段可能按照顺序开始,但是并不是说一个接一个阶段完成后才开始,一个阶段的进行完全可能激活另一个阶段的进行,交叉混合式的进行。

对于类加载的第一个阶段即加载阶段的时机并没有强制要求,不过对于类的初始化阶段有且5种情况必须进行对类进行初始化。

1.到new(使用new 关键字实例化一个对象)、getstatic(读取一个类的静态字段)、putstatic或者invokestatic(设置一个类的静态字段)这4条指令的时候,如果累没有进行过初始化。则需要先触发其初始化。

2.使用反射进行反射调用的时候,如果类没有初始化,则需要先触发其初始化。

3.当初始化一个类的时候,如果其父类没有初始化,则需要先触发其父类的初始化。

4.程序启动需要触发main方法的时候,虚拟机会先触发这个类的初始化。

5.当使用jdk1.7的动态语言支持的时候,如果一个java.lang.invoke.MethodHandler实例最后的解析结果为REF_getStatic、REF_pusStatic、REF_invokeStatic的方法句柄(句柄中包含了对象的实例数据和类型数据,句柄是访问对象的一种方法。句柄存储在堆中),并且句柄对应的类没有被初始化,那么需要先触发这个类的初始化。

这5种属于对一个类的主动引用,除此之外所有引用类的方式都不会触发类的初始化,称为被动引用。

1.通过子类引用父类的静态字段

这种情况不会导致子类的初始化,因为对于静态字段,只有直接定义静态字段的类才会被触发初始化,子类不是定义这个静态字段的类,自然不能被实例化。

2.通过数组定义来引用类,不会触发该类的初始化。

3.常量不会触发定义常量的类的初始化

因为常量在编译阶段会存入调用常量的类的常量池中,本质上并没有引用定义这个常量的类,所以不会触发定义这个常量的类的初始化。

 

三、类加载过程

1. 加载

在加载阶段虚拟机需要完成以下三件事:

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

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

3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

对于类的加载,可以分为数组类型和非数组类型,对于非数组类型可以通过系统的引导类加载器进行加载,也可以通过自定义的类加载器进行加载。这点是比较灵活的。而对于数组类型,数组类本身不通过类加载器进行加载,而是通过Java虚拟机直接进行加载的,那么是不是数组类型的类就不需要类加载器了呢?答案是否定的。因为当数组去除所有维度之后的类型最终还是要依靠类加载器进行加载的,所以数组类型的类与类加载器的关系还是很密切的。

2. 验证

验证阶段的目的是为了确保Class字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全。虚拟机的验证阶段主要完后以下4项验证:文件格式验证、元数据验证、字节码验证、符号引用验证。

3.准备

准备阶段是正式为变量分配内存空间并且设置类变量初始值。需要注意的是,这时候进行内存分配的仅仅是类变量(也就是被static修饰的变量),实例变量是不包括的,实例变量的初始化是在对象实例化的时候进行初始化,而且分配的内存区域是Java堆。这里的初始值也就是在编程中默认值,也就是零值。

4.解析

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

5.初始化

到了初始化阶段,虚拟机才开始真正执行Java程序代码,前文讲到对类变量的初始化,但那是仅仅赋初值,用户自定义的值还没有赋给该变量。只有到了初始化阶段,才开始真正执行这个自定义的过程,所以也可以说初始化阶段是执行类构造器方法clinit() 的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值