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

  • 来源:csdn博客

1、概述

 Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的,这为java应用程序提供高度的灵活性。

例如:如果编写一个面向接口的应用程序,可用等到运行时再指定其实际的实现类;用户可以通过java预定义的和自定义的类加载器,让一个本地的应用程序可以在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分。

2、类加载的时机

类的生命周期:

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

那么什么情况下需要开始类初始化呢,这就不得不涉及两个概念:主动引用和被动引用。根据Java虚拟机的规范,只有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. 通过数组定义来引用类,不会触发该类的初始化  例如, Clazz[] arr = new Clazz[10];并不会触发。

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

对于这5种主动引用会触发类进行初始化的场景,在java虚拟机规范中限定了“有且只有”这5种场景会触发类的加载。

3、类加载的过程

4、类加载器