共计 2233 个字符,预计需要花费 6 分钟才能阅读完成。
简介
有了 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>。
只有上面的几种状况,类或者接口才会被初始化:
- 执行须要援用类或者接口的 java 虚拟机指令(new,getstatic, putstatic, invokestatic)的时候。
- 首次调用 java.lang.invoke.Methodhandle 实例的时候。
- 调用类库中的某些反射办法的时候。
- 对类的某个子类进行初始化的时候。
- 被选定为 java 虚拟机启动时候的初始类的时候。
总结
class 文件通过加载,链接和初始化之后,就能够提供给 JVM 在运行时应用了。
本文作者:flydean 程序那些事
本文链接:http://www.flydean.com/jvm-class-load-link-ini/
本文起源:flydean 的博客
欢送关注我的公众号: 程序那些事,更多精彩等着您!