通过前面的学习,我们了解了Class文件的结构,在Class文件中描述的各类信息,最终都需要加载到虚拟机中之后才能被运行和使用。

接下来,我们开始学习JVM的类加载。

一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading),其中验证、准备、解析三个部分统称为连接(Linking)。

《Java虚拟机规范》 严格规定了有且只有六种情况必须立即对类进行“初始化”:

  • 1)遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化阶段。
  • 2)使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化。
  • 3)当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  • 4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
  • 5)当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
  • 6)当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

这六种场景中的行为称为对一个类型进行主动引用。

接下来我们来详细学习Java虚拟机中类加载的全过程,即加载、验证、准备、解析和初始化。

1、加载

加载是JVM加载的起点,具体什么时候开始加载,《Java虚拟机规范》中并没有进行强制约束,可以交给虚拟机的具体实现来自由把握。

在加载过程,JVM要做三件事情:

  • 1)通过一个类的全限定名来获取定义此类的二进制字节流。

  • 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

  • 3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

加载阶段结束后,Java虚拟机外部的二进制字节流就按照虚拟机所设定的格式存储在方法区之中了,方法区中的数据存储格式完全由虚拟机实现自行定义,《Java虚拟机规范》未规定此区域的具体数据结构。

类型数据妥善安置在方法区之后,会在Java堆内存中实例化一个java.lang.Class类的对象, 这个对象将作为程序访问方法区中的类型数据的外部接口。

方法区在JDK不同版本的具体实现就不再详细说了。在JDK1.8中,类型数据存储在元空间中。

2、验证

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。

验证阶段大致上会完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证。

  • 文件格式验证

第一阶段要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。需要验证魔数、版本号、常量池常量类型是否支持、指向常量的索引值等等。

  • 元数据验证

第二阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java语言规范》的要求,包括类是否有父类、父类是否继承了final修饰的类、非抽象类是否实现了父类定义的方法、类是否与父类有矛盾等等。

  • 字节码验证

第三阶段是整个验证过程中最复杂的一个阶段,主要目的是通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。

  • 符号引用验证

最后一个阶段的校验行为发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在连接的第三阶段——解析阶段中发生。

符号引用验证主要验证类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。

3、准备

准备阶段是给静态变量分配内存并设置类变量初始值的阶段。

在JDK 7及之前,这些变量的内存在方法区(永久代)中分配,在JDK 8及之后,静态变量则会随着Class对象一起存放在Java堆中。

4、解析

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。

  • 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
  • 直接引用(Direct References):直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

5、初始化

类的初始化阶段是类加载过程的最后一个步骤,在这个阶段,会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。

准备阶段,变量被赋的是系统要求的零值,在初始化阶段,赋的是代码里编写的值。

好了,基本的类加载过程已经了解完了,接下来,我们将学习负责完成加载阶段的类加载器。


参考:

【1】:《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》

【JVM进阶之路】十三:类加载过程的更多相关文章

  1. 【JVM进阶之路】一:Java虚拟机概览

    1.Java简史 Java语言是一门通用的.面向对象的.支持并发的程序语言.全球从事Java相关开发的人员已经数以百万计. 从1995年"Java"正式出现以来,Java已经经历了 ...

  2. 深入理解JVM(③)虚拟机的类加载过程

    前言 上一篇我们介绍到一个类的生命周期大概分7个阶段:加载.验证.准备.解析.初始化.使用.卸载.并且也介绍了类的加载时机,下面我们将介绍一下虚拟机中类的加载的全过程.主要是类生命周期的,加载.验证. ...

  3. 【JVM进阶之路】十四:类加载器和类加载机制

    在上一章里,我们已经学习了类加载的过程,我们知道在加载阶段需要"通过一个类的全限定名来获取描述该类的二进制字节流",而来完成这个工作的就是类加载器(Class Loader). 1 ...

  4. JVM进阶之路(一)

    JVM是Java程序运行的基本,了解Java程序的运行原理绝对有必要了解JVM的内容,JVM的内容其实很多,而且版本不同,其中的内容也很多不同,接下来就通过几篇文章来慢慢讲述JVM的内容. 一.Jvm ...

  5. 【JVM进阶之路】七:垃圾收集器盘点

    在前面,我们已经了解了JVM的分代收集,知道JVM垃圾收集在新生代主要采用标记-复制算法,在老年代主要采用标记-清除和标记-整理算法.接下来,我们看一看JDK默认虚拟机HotSpot的一些垃圾收集器的 ...

  6. 【JVM进阶之路】八:性能监控工具-命令行篇

    定位问题的时候,知识.经验是关键基础,数据是依据,工具是运用知识处理数据的手段. 在实际的故障排查.性能监控中,常常是操作系统的工具和Java虚拟机的工具结合使用. 1.操作系统工具 1.1.top: ...

  7. 【JVM进阶之路】十:JVM调优总结

    1.调优原则 JVM调优听起来很高大上,但是要认识到,JVM调优应该是Java性能优化的最后一颗子弹. 比较认可廖雪峰老师的观点,要认识到JVM调优不是常规手段,性能问题一般第一选择是优化程序,最后的 ...

  8. JVM类加载过程学习总结

    JVM类加载过程学习总结 先不说JVM类加载的原理,先看实例: NormalTest类,包含了一个静态代码块,执行的任务就是打印一句话. /** * 在正常类加载条件下,看静态代码块是否会执行 * @ ...

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

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

随机推荐

  1. 2. linux下如何上传和下载文件

    一. 安装工具包 yum install -y lrzsz lrzsz是一个unix通信套件提供的X,Y,和ZModem文件传输协议,可以用在windows与linux 系统之间的文件传输,体积小速度 ...

  2. 我与Git的那些破事(上)--代码管理

    1. Git是什么? 作为一名程序猿,我相信大家都或多或少接触过git--分布式版本控制软件. 有人说,它是目前世界上最先进的分布式版本控制系统,我想说,是否最先进不知道,但确实好用,实用. 作为一款 ...

  3. 超详细网站博客域名和二级域名、子域名升级HTTPS免费申请SSL证书配置nginx指南

    随着互联网的飞速发展,我们的工作生活已经离不开互联网,HTTP虽然使用极为广泛, 但是存在不小的安全缺陷, 主要是其数据的明文传送和消息完整性检测的缺乏, 而这两点恰好是网络支付,网络交易等网站应用中 ...

  4. 1049 Counting Ones

    The task is simple: given any positive integer N, you are supposed to count the total number of 1's ...

  5. 仅用一句SQL更新整张表的涨跌幅、涨跌率

    问题场景 各大平台店铺的三项评分(物流.服务.商品)变化情况: 商品每日价格的变化记录: 股票的实时涨跌浮: 复现场景 表:主键ID,商品编号,记录时的时间,记录时的价格,创建时间. 问题:获取每个商 ...

  6. Unittest单元测试框架——BeautifulReport测试报告和Yagmail自动发送邮件

    一.前言 之前在做appium自动化的时候,已经提到过unittest框架的基本概念.用例执行,以及BeautifulReport测试报告的简单使用了(地址:https://www.cnblogs.c ...

  7. php swoole 和 websocket的初次碰撞

    php swoole 扩展仿佛为php开发打开了一扇窗户 阅读文档 https://wiki.swoole.com php workman和swoole原来是两个东东 swoole的使用范围更广,能做 ...

  8. 通过中转DLL函数实现DLL劫持

    当我们运行程序时,一般情况下会默认加载Ntdll.dll和Kernel32.dll这两个链接库,在进程未被创建之前Ntdll.dll库就被默认加载了,三环下任何对其劫持都是无效的,除了该Dll外,其他 ...

  9. POJ1486模拟或者匈牙利变种

    题意:       有n个矩形,每个矩形上的某个位置上都有一个点,但是由于矩形是透明的,当一些矩形重叠在一起的时候就很可能分不清哪个点是那个矩形的,给你n个矩形的坐标,还有n个点的坐标,然后让你找出所 ...

  10. Linux下用SUID提权

    关于SUID详细:Linux下的用户.组和权限 SUID可以让调用者以文件拥有者的身份运行该文件,所以我们利用SUID提权的思路就是运行root用户所拥有的SUID的文件,那么我们运行该文件的时候就得 ...