Android技术知识Android开发Android开发

五、JVM 系列(3) —— 类加载机制

2018-10-13  本文已影响14人  Yjnull

Java 与 C++ 之间有一堵由 内存动态分配和垃圾收集技术 所围成的 “高墙”,墙外面的人想进去,墙里面的人却想出来。

类的生命周期如下:


类的生命周期.png

0. 什么时候开始类加载过程的第一个阶段: 加载?

Java 虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。
但是对于 初始化阶段,虚拟机规范则是严格规定了有且只有5中情况必须立即对类进行初始化(而加载、验证、准备、自然要在此之前开始)。

上述这5种场景中的行为称为对一个类进行 主动引用。 除此之外,所有引用类的方式都不会触发初始化,称为被动引用。

1. 加载

虚拟机在加载阶段,需要完成以下3件事情:

对于上述 二进制字节流,不一定要从一个 Class 文件中获取,还可以有如下来源:

注意: 数组类 本身不通过类加载器创建,它是由 Java 虚拟机直接创建的。 但是数组类的元素类型最终还是要靠类加载器去创建的。

2. 验证

验证是 连接阶段(Linking)的第一步。 它的目的是 为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求。 不会危害虚拟机自身的安全。
比如:纯粹的 Java 代码无法做到 访问数组边界以外的数据、将一个对象转型为它并未实现的类型、跳转到不存在的代码行 之类的事情,如果你这样做了,编译器将会 拒绝编译,也就是无法生成 Class 文件。
但是前面说过,Class 文件并不一定要由 Java 源码编译而来,可以使用任何途径产生,甚至包括用十六进制编辑器直接编写来产生 Class 文件。然而在 字节码语言 层面上,上述 Java 代码无法做到的事情都是可以实现的,所以 验证阶段 是非常重要也是必要的。

验证阶段 大致会完成下面4个阶段的 检验动作。

2.1 文件格式验证

只有通过了这个阶段的 验证后,字节流才会进入 内存的方法区 中进行存储。

2.2 元数据验证

经过 文件格式验证,字节流已经加载到 方法区, 这个阶段的工作就是 **对方法区的字节码 **描述的信息进行语义分析,以保证其描述的信息符合 Java 语言规范的要求, 验证点如下:

2.3 字节码验证

对类的方法体进行校验。

2.4 符号引用验证

目的是确保 解析动作能正常执行。 校验点:

对于虚拟机的类加载机制来说,验证阶段非常重要的,但不是一定必要的。如果所运行的全部代码(包含自己编写以及第三方包的代码)都已经被反复使用和验证过,那么可以考虑使用 -Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

3. 准备

准备阶段是正式为 类变量(static修饰的变量) 分配内存并设置 类变量初始值 的阶段。 这些变量所使用的内存都将在 方法区 中进行分配。

3.1 类变量:赋予初始值
假设: public static int value = 123;
变量 value 在准备阶段过后的初始值为 0 而不是 123。 把value赋值为123的动作将在 初始化阶段 执行。

3.2 常量:赋予真实值
假设:public static final int value = 123;
编译时,javac 将会为 value 生成 ConstantValue 属性,在准备阶段 虚拟机就会根据 ConstantValue 的设置将 value 赋值为 123

3.3 实例变量:不赋任何值
该阶段仅对类变量进行内存分配,而对于实例变量(或者称呼为成员变量)并不会分配内存,也就更不用提赋值的事。实例变量的初始化,是随着对象实例化时在Java堆上分配内存而进行的。

4. 解析

主要工作是: 虚拟机将常量池内的 符号引用 替换为 直接引用 的过程。

5. 初始化

主要工作:执行类构造器 <clinit>() 方法 (class init 的简称)。

6. 类加载器——双亲委派模型

类加载器从 Java 开发人员的角度可以分为以下三种系统提供的类加载器:

双亲委派模型.png

上图所示的 类加载器 之间的这种层次关系,称为 类加载器的双亲委派模型。

双亲委派模型的工作过程:当一个类加载器收到了类加载的请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时(它的搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。

上一篇 下一篇

猜你喜欢

热点阅读