# 类的加载、链接和初始化（基于Java 1.8）


Java的数据类型（Data type）主要是有两种：
1. [基本类型 primitive types](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.3)
2. [引用类型 reference types](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.4)

其中引用类型又被细分为：
1. 类 class types
2. 数组 array types
3. 接口 interface types

基本类型和数组类型是由Java虚拟机直接生成的，其他（类 class types、接口 interface types）则需要Java虚拟机对其进行链接和初始化。

# 1. Java虚拟机启动 Java Virtual Machine Startup

- 通过引导类加载器创建一个初始类来启动，并执行这个`public class`中的`void main(String[])`方法。
- 初始类可以作为命令行参数提供。或者，该实现可以提供一个初始类，该初始类设置一个类加载器，该类加载器进而加载。

# 2. 创建和加载 Creation and Loading

- 指查找字节流，并据此创建类的过程。
- 其中数组（array types）是没有字节流的，由Java虚拟机直接生成，对于其他的类来说，Java虚拟机需要借助于类加载器来完成查找字节流的工程。

## 2.1 类加载器 ClassLoader

在Java虚拟机规范中，类加载器被分为两种：
1. Java虚拟机提供的引导类加载器（bootstrap class loader)
2. 用户定义的类加载器(user-defind class loaders)

### 2.1.1 Java虚拟机提供的引导类加载器 bootstrap class loader

- bootstrap class loader 由Java虚拟机提供的
- 这个类加载器是使用C++实现的，没有对应的Java对象。

### 2.1.2 用户定义的类加载器 user-defind class loaders

- user-defind class loaders 是Java虚拟机规范中对于类加载器的分类划分，是一个统称，实际上并没有这个类加载器
- 用户定义的类加载器都是`java.lang.ClassLoader`类的子类
- 在Java虚拟机规范中提到，用户定义的类加载器可以实现通过网络下载类，动态生成类或从加密文件中提取类
- 用户定义的类加载器需要由bootstrap class loader去加载
- 在Java1.8的核心类库中，提供了两个类加载器，分别是：
	1. 扩展类加载器 extention class loader 
	`sun.misc.Launcher.ExtClassLoader`
	3. 应用类加载器 application class loader
	`sun.misc.Launcher.AppClassLoader`

#### 2.1.2.1 扩展类加载器 extention class loader

- 扩展类加载器的父是启动类加载器（bootstrap class loader)
- 负责加载相对次要、但又通用的类，如JRE的lib/ext目录下jar包中的类（以及java.ext.dirs指定的类, 这个可以通过查看`sun.misc.Launcher.ExtClassLoader.getExtDirs()`方法确认）

#### 2.1.2.2 应用类加载器 application class loader

- 应用类加载器的父则是扩展类加载器
- 负责加载应用程序路径下的类（应用程序指虚拟机参数-cp/-classpath、系统变量java.class.path或环境变量CLASSPATH所指定的路径。这个可以通过查看`sun.misc.Launcher.AppClassLoader`确认）
- 默认情况下，应用程序中包含的类便是通过应用类加载器加载的。

### 2.1.3 双亲委派模型

- 指的是一个类加载器接收到加载请求时，会先将请求转发给父类加载器，在父类加载器没有找到所请求的类的情况下，该类加载器才会去尝试加载。

- 双亲委派模型可以避免类的重复加载，以及java的核心api被篡改的问题。

# 3 链接 Linking

- 指将创建的类合并至Java虚拟机中，使之能够执行的过程。可分为验证(Verification)、准备(Preparation)以及解析(Resolution)三个阶段

## 3.1 验证 Verification

验证是为了确保被加载的类满足Java虚拟机的约束条件。

## 3.2 准备 Preparation

- 准备是为被加载的类的静态字段分配内容。
- 构造其他跟类层次相关的数据结构：如用来实现虚方法的动态绑定的方法表。

## 3.3 解析 Resolution

> 在开始解析之前，需要知道：
class文件被加载到Java虚拟机之前，这个类无法知道其他类及其方法、字段所对应的具体地址，甚至不知道自己方法、字段的地址。因此，每当需要引用这些成员时，Java编译器会生成一个符号引用。在运行阶段，这个符号引用一般都能无歧义地定位到具体目标上。

- 解析的目标是将符号引用解析成为实际应用： 如果符号引用指向一个未被加载的类，或者未被加载类的字段或方法，那么解析就触发这个类的加载。（但未必会出发这个类的链接和初始化）

此外，在Java虚拟机规范中并没有要求在链接过程中完成解析。仅规定了：如果某些字节码使用了符号引用，那么在执行这些字节码之前，需要完成对这些符号引用的解析。

# 4. 初始化 Initialization

- 为标记为常量值的字段赋值，以及执行`<clinit>`方法的过程
- Java虚拟机会通过加锁来确保类的`<clinit>`方法仅被执行一次

> 常量值解释：
> Java代码中，如果要初始化一个静态字段，可以在声明时直接赋值，或者在静态代码块中对其进行赋值
> 如果直接赋值的静态字段被final所修饰，并且它的类型是基本类型或字符串时，该字段便会被Java编译器标记为常量值（ConstantValue）

## 4.1 初始化的触发条件

在Java虚拟机规范中明确枚举了以下情况：

- The execution of any one of the Java Virtual Machine instructions new, getstatic, putstatic, or invokestatic that references C (§new, §getstatic, §putstatic, §invokestatic). These instructions reference a class or interface directly or indirectly through either a field reference or a method reference.

- Upon execution of a new instruction, the referenced class is initialized if it has not been initialized already.

- Upon execution of a getstatic, putstatic, or invokestatic instruction, the class or interface that declared the resolved field or method is initialized if it has not been initialized already.

- The first invocation of a java.lang.invoke.MethodHandle instance which was the result of method handle resolution (§5.4.3.5) for a method handle of kind 2 (REF_getStatic), 4 (REF_putStatic), 6 (REF_invokeStatic), or 8 (REF_newInvokeSpecial).

- This implies that the class of a bootstrap method is initialized when the bootstrap method is invoked for an invokedynamic instruction (§invokedynamic), as part of the continuing resolution of the call site specifier.

- Invocation of certain reflective methods in the class library (§2.12), for example, in class Class or in package java.lang.reflect.

- If C is a class, the initialization of one of its subclasses.

- If C is an interface that declares a non-abstract, non-static method, the initialization of a class that implements C directly or indirectly.

- If C is a class, its designation as the initial class at Java Virtual Machine startup (§5.2).

Java虚拟机规范中有部分是依赖于Java虚拟机指令了，我对此了解并不多，以下摘抄于《极客时间-深入拆解Java虚拟机-郑雨迪》的分享，相较而言更通俗易懂些。

1. 当虚拟机启动时，初始化用户指定的主类；
2. 当遇到用以新建目标类实例的new指令时，初始化new指令的目标类；
3. 当遇到调用静态方法的指令时，初始化该静态方法所在的类；
4. 当遇到访问静态字段的指令时，初始化该静态字段所在的类；
5. 子类的初始化会触发父类的初始化；
6. 如果一个接口定义了default方法，那么直接实现或者间接实现该接口的类的初始化，会触发该接口的初始化；
7. 使用反射API对某个类进行反射调用时，初始化这个类；
8. 当初次调用MethodHandle实例时，初始化该MethodHandle指向的方法所在的类。

# 5. 绑定本机方法实现

指的是将Java编程语言以为的其他语言编写的功能和实现native方法的功能集成到Java虚拟机中以便可以执行的过程。

传统上来说，此过程可称为链接，但Java虚拟机规范中指出，使用绑定是为了避免于Java虚拟机对类和接口的链接产生混淆。

# 6. Java虚拟机退出

当调用`Runtime.exit()`、`Runtime.halt()`、`System.exit`，并且`SecurityManager`安全管理其允许exit或halt时候，Java虚拟机就会被关闭。

同时，JNI（Java Native Interface）规范描述了对于JNI调用相关的java虚拟机的终止信息。

# 参考文档

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
https://time.geekbang.org/column/article/11523
