JVM(五):探究类加载过程-上

本文我们来研究一个Java字节码文件(Class文件)是如何加载入内存中的,在這個过程中涉及类加载过程中的加载,验证,准备,解析(连接),初始化,使用,销毁过程,并探讨实行这些过程的类加载器,以及其加载的逻辑。

概述

Java拥有动态加载类和动态连接的特性,因此其加载过程并不像其他语言在编译时就已经完成,它是动态进行的,即在程序运行过程中动态加载入内存中。

加载过程

在这里需要记住的是,图中的顺序说明的是阶段开始的顺序,并不是后面的阶段需要等到前面的执行完成后才能够执行,其在运行过程中是一个交叉混合执行的过程。

此外解析阶段也是一个特殊的阶段,为了支持Java语言的动态绑定,很多时候 Java 只要在运行后才能知道实际调用的对象是什么,因此解析阶段有时是开始在初始化后的。

加载

加载阶段完成的是将虚拟机外部的二进制字节流按照虚拟机所需的格式存储在方法区之中。而为了完成这步需要完成哪些功能呢:

  1. 通过一个类的全限定名获取二进制流;
  2. 将二进制流定义的静态存储结构转化为方法区的运行时数据结构;
  3. 在内存中生成一个代表这个类的 Class 对象,作为方法区数据的访问接口。

需要注意的是,上面所说的3个步骤,都只是规范要求的部分,这个要求其实是比较松的,很多东西并没有限制的很死,比如说第一步的获取二进制流,其并没有要求二进制流必须从Class文件获取,因此在使用过程中类的二进制流可以从网络获取,可以动态计算生成等等。

验证

验证作用是确保文件的字节流包含信息符合当前虚拟机要求,保证其并不会危害虚拟机的安全。因为以前说过 Class 文件并不都是源码编译而来的,人是可以手动修改生成 Class 文件的,因此这一步验证工作就十分有必要了。那么验证都需要验证哪些地方呢:

文件格式验证

这一步主要是保证Class文件格式上符合Java信息的要求。例如文件类型,版本号,常量池,常量池数据等等。。。。。。

此外在这一步字节流就会进入内存的方法区之中了,后面的操作都是基于方法区内的存储结构进行的。

元数据验证

对字节码描述信息进行语义分析,例如类是否有父类,重载是否正确,final,abstract有没有用错等,其主要目的是对类的元数据进行语义分析,保证符合Java语言规范。

字节码验证

对数据流和控制流进行分析。例如字节码指令集的正确,程序跳转的安全。其主要目的是检查方法体内的数据安全,确保程序语义合法,符合逻辑。

符号引用验证

符号引用验证也是一个比较特殊的阶段,其为解析阶段服务(这也验证了前面所说的,这几个过程并不是依次执行完成的)。在解析过程中,虚拟机将符号引用转换为直接引用,其主要是对常量池中的各种符号引用做匹配性校验。检验内容包括以下几个:

  1. 符号引用指向的类能否找到;
  2. 指定的类有没有描述的方法和字段;
  3. 符号引用指向的各种信息的访问权限是不是对的;
  4. 。。。。。。。。。。。。。。。

准备

类变量(被static修饰的变量)分配内存并设置类变量初始值。

这里需要注意的是设初始值值得是为其设置零值,例如数值量的 0,boolean 值的 false 等。但是特殊情况下,如类变量是一个常量,那么在准备阶段,虚拟机就会将其设置为常量指代的值。

解析

在验证阶段的符号引用验证说过解析阶段就是将符号引用转换为直接引用,那么符号引用和直接引用分别指什么呢,他们之间又有何区别:

  • 符号引用。是能够无歧义定位目标的任何形式的字面量,其与虚拟机实现的内存布局无关,引用的目标不一定需要加载入内存中;
  • 直接引用。可以直接指向目标指针,偏移量的引用,其和虚拟机实现的内存布局相关,引用的目标一定需要在内存中。

在这一步虚拟机会将类/接口,字段,类方法,接口方法等进行解析,变为直接引用。

初始化

初始化阶段主要是初始化类变量和其他资源,主要是通过<clint>()方法。

<clint>()是通过编译器自动收集所有类变量的赋值动作和静态语句块(static{}块)并按照顺序合并生成的。

static块可以为前面未定义的变量赋值,但无法访问

	 static{
i = 111;
// 下面语句无法编译通过,会提示Illegal forward reference
// System。out。println(i);
}
static int i = 0;
public static void main(String[] args){
System。out。println(i);`
}

程序输出为0,因为其初始化操作是按照顺序进行的,但如果这里static int i;,不为其赋值,那么结果就是111。

虚拟机并没有要求什么时候进行其他阶段的工作,但初始化阶段不同。当发生一下几种情况时,虚拟机必须要开始初始化工作。(作为初始化前的加载,验证,准备,解析也就都按部就班开始了)。

  1. 当在字节码层面遇到以下指令时,new(对象都要生成了,肯定要初始化了),get/put static(使用静态变量了,肯定要赋值了),invoke static(调用静态方法了都,肯定要为静态量赋值);
  2. 反射调用。当使用java。lang。reflect中的方法对类进行反射调用;
  3. 初始化一个类的时候,发现父类还有初始化,那么需要先初始化其父类,(父接口不用立即初始化,只有使用到其常量时,才需要将其初始化);
  4. 虚拟机需要一个入口,因此主类需要初始化;
  5. 动态方法解析,解析出方法是其他类的静态方法,那么需要将其初始化。

虚拟机规定有且仅有以上5种方法需要立即初始化,还有一些调用,看起来像需要初始化,但其实并不需要,可以称之为被动调用

  1. 子类直接使用父类的静态变量。虚拟机规定只有直接定义静态变量的类需要初始化,因此这种情况下,只会触发父类的初始化,而子类并不会触发;
  2. 数组对象。当定义对象数组时,只会触发数组类的初始化,其内的对象的类并不会初始化;
  3. 当一个类调用另一个类的常量时。此时并不会对常量类(被调用者)进行出初始化。只会将调用者初始化。因为在编译阶段,根据常量传播优化,会将常量类的常量放置到调用者的常量池中,此时这两个类已经没有了瓜葛,因此也就不存在将其初始化了。

总结

在本文中着重介绍了一个类加载入内存中的各个阶段过程,了解这个阶段过程可以明白虚拟机是如何将一个静态的类文件,经过一系列的动作变为 Java 内存中的各种数据结构。

在下一篇文章我们将会介绍执行加载阶段的主体,类加载器,明白类加载器的模型以及其背后的逻辑,并尝试自定义一个类加载器,来完成加载工作。

文章在公众号 "iceWang" 第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

本系列文章主要借鉴自《深入分析 JavaWeb 技术内幕》和《深入理解 Java 虚拟机- JVM 高级特性与最佳实践》。

JVM(五):探究类加载过程-上的更多相关文章

  1. JVM(六):探究类加载过程-下

    JVM(六):探究类加载过程-下 上文说了类加载过程的5个阶段,着重介绍了各个阶段做的工作.在本文中,我们对执行加载阶段的主体进行探讨,学习类加载器的模型和逻辑,以及我们该如何自定义一个类加载器. 定 ...

  2. JVM总括四-类加载过程、双亲委派模型、对象实例化过程

    JVM总括四-类加载过程.双亲委派模型.对象实例化过程 目录:JVM总括:目录 一. 类加载过程 类加载过程就是将.class文件转化为Class对象,类实例化的过程,(User user = new ...

  3. JVM学习笔记——类加载过程

    JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...

  4. [jvm学习笔记]-类加载过程

    JVM类加载的过程 加载=>验证=>准备=>解析=>初始化 5个阶段所执行的具体动作 加载 在加载阶段,虚拟机需要完成3个事情1.通过一个类的全限定名获取定义此类的二进制字节流 ...

  5. JVM运行和类加载过程

    JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) (该知识点引用 http://www.cnblogs.com/dingyingsi/p/3760730.h ...

  6. JVM探究之 —— 类加载过程

    1. 类加载是什么 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 与那些在编译时需要进行连 ...

  7. JVM学习十五 - (复习)类加载的时机、类加载过程、类加载器

    一.类加载的时机 类的生命周期 类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括以下 7 个阶段: 加载 验证 准备 解析 初始化 使用 卸载 验证.准备.解析 3 个阶段统称为连接 ...

  8. jvm学习002 虚拟机类加载过程以及主动引用和被动引用

    虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存中开始,到卸载出内存为 ...

  9. 【深入Java虚拟机】一 JVM类加载过程

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

随机推荐

  1. 使用iconfont管理项目中的字体图标

    先来说说字体图标的好处: 很容易任意地缩放: 很容易地改变颜色: 很容易地产生阴影: 可以拥有透明效果: 一般来说,有先进的浏览器支持: 可以使用CSS来装饰(可以得到CSS很好支持): 可以快速转化 ...

  2. Android开发中使用数据库时出现java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.

    最近在开发一个 App 的时候用到了数据库,可是在使用数据库的时候就出现了一些问题,在我查询表中的一些信息时出现了一下问题: Caused by: java.lang.IllegalStateExce ...

  3. addslashes,stripslashes

    官方介绍: (PHP 4, PHP 5) addslashes — 使用反斜线引用字符串 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线.这些字符是单引号(’).双引号(”). ...

  4. Node.js——body方式提交数据

    引入核心模块 http,利用其 api(http.createServer) 返回一个 http.server 实例,这个实例是继承于net.Server,net.Server 也是通过net.cre ...

  5. [转载]迅为4418开发板Qt移植移动4G模块第一部分

        本文转自迅为论坛:http://topeetboard.com   平台:iTOP-4418开发板   1.首先要配置内核,这个一步和Android系统移植3G或者4G模块是一样的.一般模块的 ...

  6. 前复权是从今天的价格倒推 后复权是从上市价格前推 不复权就是原始K线。

    前复权是从今天的价格倒推 后复权是从上市价格前推 不复权就是原始K线.

  7. 【原创】DESTOON做中英双语言(多语言)切换版本具体详解

    第一次发原创好激动,该注意点什么? 在开发过程中用户有许多要求,比如这个多语言切换就是一个需求. 首先讲解一下DESTOON(DT)后台系统如何做这个中英.甚至多语言切换的这个功能. DT本身不自带多 ...

  8. COPY - 在表和文件之间拷贝数据

    SYNOPSIS COPY tablename [ ( column [, ...] ) ] FROM { 'filename' | STDIN } [ [ WITH ] [ BINARY ] [ O ...

  9. java 解析json格式数据

    有时候可能会用到json格式进行数据的传输,那么怎么把接收到的数据解析出来呢? 下面介绍两种解析json数据的方法: 1.通过谷歌的Gson来进行解析: json数据:sTotalString = { ...

  10. Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用

    近来由于项目需要,需要用到手机短信验证码的功能,其中最主要的是用到了第三方提供的短信平台接口WebService客户端接口,下面我把我在项目中用到的记录一下,以便给大家提供个思路,由于本人的文采有限, ...