简介

有了java class文件之后,为了让class文件转换成为JVM能够真正运行的构造,须要经验加载,链接和初始化的过程。

这三个过程是怎么工作的呢?在本文中你将会找到答案。

加载

JVM能够分为三大部分,五大空间和三大引擎,要讲起来也不是特地简单,先看上面的总体的JVM架构图。

从下面的图中,咱们能够看到JVM中有三大部分,别离是类加载零碎,运行时数据区域和Execution Engine。

加载就是依据特定名称查找类或者接口的二进制示意,并依据此二进制示意来创立类和接口的过程。

运行时常量池

咱们晓得JVM中有一个办法区的区域,在JDK8中,办法区的实现叫做元空间。这个元空间是寄存在本地内存中的。

办法区中寄存着每个class对应的运行时常量池。

当类或者接口创立的时候,就会通过class文件中定义的常量池来构建运行时常量池。

运行时常量池中有两种类型,别离是symbolic references符号援用和static constants动态常量。

其中动态常量不须要后续解析,而符号援用须要进一步进行解析解决。

动态常量分为两个局部:String常量和数字常量。

String常量是对String对象的援用,是从class中的CONSTANT_String_info构造体构建的。

数字常量是从class文件中的CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info和 CONSTANT_Double_info 构建的。

符号援用也是从class中的constant_pool中构建的。

对class和interface的符号援用来自于CONSTANT_Class_info。

对class和interface中字段的援用来自于CONSTANT_Fieldref_info。

class中办法的援用来自于CONSTANT_Methodref_info。

interface中办法的援用来自于CONSTANT_InterfaceMethodref_info。

对办法句柄的援用来自于CONSTANT_MethodHandle_info。

对办法类型的援用来自于CONSTANT_MethodType_info。

对动静计算常量的符号援用来自于CONSTANT_MethodType_info。

对动静计算的call site的援用来自于CONSTANT_InvokeDynamic_info。

类加载器

类是怎么创立的呢?类的创立能够是由其余类调用该类的初始化办法来创立,也能够通过反射来创立。

类其实又能够分为两种,一种是数组类,一种是非数组类。

对于非数组类,因为他们有相应的二进制示意,所以是通过类加载器加载二进制示意来创立的。

而对于数组类,因为他们没有内部的二进制示意,所以数组类是由java虚拟机创立的。

java虚拟机中的类加载器又有两种,一种是虚拟机提供的疏导类加载器,一种是用户自定义的类加载器。

如果是用户自定的类加载器,那么应该是ClassLoader的一个实现。用户自定义类加载器次要是为了扩大java虚拟机的性能,以反对动静加载并创立类。

链接

链接是为了让类或者接口能够被java虚拟机执行,而将类或者接口并入虚拟机运行时状态的过程。

链接具体的工作包含验证和筹备类或者接口。而解析这个类或者接口中的符号援用是链接过程中的可选局部。

如果java虚拟机抉择在用到类或者接口中的符号援用时才去解析他们,这叫做提早解析。

如果java虚拟机在验证类的时候就解析符号援用,这就叫做事后解析。

验证

验证次要是为了保障类和接口的二进制示意的构造正确性。

如果类或者接口的二进制示意不满足相应的束缚,则会抛出VerifyError异样。

筹备

筹备次要是创立类或者接口的动态字段,并应用默认值来初始化这些字段。

解析

解析是指依据运行时常量池中的符号援用来动静决定其具体值的过程。

在执行java虚拟机指令:

anewarray,checkcat, getfield, getstatic, instanceof, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldc, ldc_w, multianewarray, new , putfield和putstatic这些指令的时候,都会去将符号援用指向运行时常量池,从而须要对符号援用进行解析。

解析能够分为类和接口的解析,字段解析,一般办法的解析,接口办法解析,办法类型和办法句柄解析,调用点限定符解析这几种。

初始化

类或者接口的初始化是指执行类或者接口的初始化办法<clinit>。

只有上面的几种状况,类或者接口才会被初始化:

  1. 执行须要援用类或者接口的java虚拟机指令(new,getstatic, putstatic, invokestatic)的时候。
  2. 首次调用java.lang.invoke.Methodhandle实例的时候。
  3. 调用类库中的某些反射办法的时候。
  4. 对类的某个子类进行初始化的时候。
  5. 被选定为java虚拟机启动时候的初始类的时候。

总结

class文件通过加载,链接和初始化之后,就能够提供给JVM在运行时应用了。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jvm-class-load-link-ini/

本文起源:flydean的博客

欢送关注我的公众号:程序那些事,更多精彩等着您!