JVM之类装载系统

253 阅读4分钟

java文件转化为Class文件后,虚拟机是怎样加载这些Class文件的呢? 
Class文件被载入虚拟机后,会做哪些处理?
类加载的具体步骤是怎么样的?

Class文件的装载流程

JVM运行机制

Class文件装载流程

类装载条件

Class文件只有在必须要使用的时候才会被装载,Java虚拟机不会无条件地装载Class类型。

一个类或接口在初次使用前,必须进行初始化。

这里的“使用”是指主动使用,主动使用只有下列几种情况:

  • 当创建一个类的实例时,比如使用new关键字或者反射、克隆、反序列化。 
  • 当调用类的静态方法时,即使用了字节码invokestatic指令。 
  • 当使用类或接口的静态字段时(final常量除外),比如使用getstatic或者 putstatic指令。 
  • 当使用java.lang.reflect包中的方法反射类的方法时。 
  • 当初始化子类时,要求先初始化父类。 
  • 作为启动虚拟机,含有main()方法的那个类。 

除了以上的情况属于主动使用,其他情况都属于被动使用。被动使用不会引起类的初始化。

加载类

加载类处于类装载的第一个阶段。

加载类工作:

  • 通过类的全名获取类的二进制数据流。 
  • 解析类的二进制数据流为方法区内的数据结构。
  • 创建java.lang.Class类的实例,表示该类型。

在获取类的二进制信息后,Java虚拟机就会处理这些数据,并最终转为一个java.lang.Class 的实例,java.lang.Class实例是访问类型元数据的接口,也是实现反射的关键数据。通过Class 类提供的接口,可以访问一个类型的方法、字段等信息。

连接

验证

  • 验证是连接操作的第一步,目的是保证加载的字节码是合法、合理并符合规范的。包括了格式检查,语义检查,字节码检查,符号引用检查。

准备

  • 验证通过就会进入准备阶段,JVM在这个阶段为这个class分配内存空间,并设置初始值。

解析

  • 准备阶段后是解析,其工作就是讲类、接口、字段和方法的符号引用转为直接引用。

初始化

类的初始化是类装载的最后一个阶段。此时,类会开始执行Java字节码。初始化阶段的重要工作是执行类的初始化方法 。方法是由编译器自动生成的,它是由类静态成员的赋值语句及static语句块共同产生的。 

ClassLoader

认识ClassLoader

ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,主要工作在class装载的加载阶段,主要作用是从系统外部获得Class二进制数据流。

ClassLoader分类

Java虚拟机会创建3类ClassLoader为整个应用程序服务,分别为:

  • BootStrap ClassLoader(启动类加载器)
  • Extension ClassLoader(扩展类加载器)
  • App ClassLoader(应用加载类)

此外还有一个应用程序拥有自定义的Classloader来扩展Java虚拟机获取Class数据的能力。

                                 

ClassLoader层次结构如图所示,从ClassLoader的层次自顶向下为启动类加载器、扩展类加载器、应用类加载器和自定义类加载器。其中,应用类加载器的双亲为扩展类加载器,扩展类加载器的双亲为启动类加载器。当系统需要使用一个类时,在判断类是否已经被加载时,会从底层类加载器开始进行判断。当系统需要加载一个类时,会从顶层类开始加载,依次向下尝试,直到成功。

ClassLoader的双亲委派机制

系统中的ClassLoader在协同工作时,默认会使用双亲委派模式。在类加载的时候,系统会判断当前类是否已经被加载,如果已经被加载,就会直接返回可用的类,否则就会尝试加载。在尝试加载时,会先请求双亲处理,如果请求失败,则会自己加载。

双亲委派机制的弊端

从上文知道检查类是否加载的委托过程是单向的,但上层的ClassLoader无法访问下层的ClassLoader所加载的类。

ClassLoader的重载

双亲模式的类加载方式是虚拟机默认的,但不一定这样做。通过重载ClassLoader可以修改此行为。很多应用软件和框架都修改了这个行为,例如Tomcat有各自独特的类加载顺序,