【类加载】入门知识点详解

类加载这部分内容比较偏理论,对初学者来说可能比较难理解。本文以较为通俗易懂的语言对类加载的过程进行阐述,相信大家看过几遍就能理解。在开始介绍本节内容前先给出相关内容的线索,脑海中有了线索不仅有利于学习理解,在面试中回答问题也会更加有条理。这些线索以若干问题的形式给出,大家学习完本章后可以自问自答,检测下学习效果:

  1. 什么是类加载?类加载如何实现?
  2. 类的生命周期,每一步都有哪些子操作?和内存模型有哪些交集?
  3. 类加载器如何实现?能否结合一些源码介绍?
  4. 双亲委派模型究竟如何被破坏了?都有哪些框架可以作为破坏的例子?
  5. 有序列表内容

2.1知识点入门理解

2.1.1 原理解释

2.1.1.1 类和对象的区别

Java是一种面向对象的语言,初识”面向对象“必然会接触到两个关键词,类和对象。类可以理解为具有相同性质实体的抽象表达,比如苹果手机、小米手机和锤子手机,它们都能打电话或上网冲浪,因此都具有手机这一概念。对象一般指一个具体的事物,它客观存在于这个世界上。一个对象必然是一个类在世界上的代理,苹果手机是手机概念的实体,一台iPhone6看得见摸得着,而你不能靠脑子中所有的“手机这一概念”与他人进行联系。

2.1.1.2 什么是类加载?为什么需要类加载?

类加载是指JVM将类文件读入内存并生成一个java.lang.Class对象的过程。下面用一系列问答梳理下类加载相关动机与原因:

  • Q:这个过程加载了什么?A:加载的是.class文件。
  • Q:.class文件又是什么?A:.class文件是我们平时写的.java经过编译后的内容。这种格式的文件描述了类的特征与结构。Java代码必须经过编译生成字节码文件,最终转换成机器码才能被执行。
  • Q:从哪里加载到哪里?A:可以从磁盘、网络以及数据库等媒介获取加载到内存中。
  • Q:加载后达成了什么目的?A:将类的结构性描述由字节描述转化内存中的结构信息,指导类实例化等操作。

类加载通常由JVM提供具体实现。类加载的目的是在于为真正的业务代码提供必要的运行环境,因此类加载通常在程序真正运行前执行,将一些程序需要的类提前加载进内存并生成Class对象。类加载可以类比为驾驶汽车,我们的目的是驾驶汽车抵达目的地,然而我们在执行这一过程中所需的第一件事则是启动该车,类加载等同于使用钥匙打火点燃发动机。试想,如果不点燃发动机(执行类加载),你将如何驱动汽车运行(Java程序执行)?

2.1.1.3 类加载如何实现?

类加载是一个较为复杂的过程,为了降低我们开发的工作量,JVM为我们提供了一些类加载器,我们一般不需要过度关心类是如何加载的,只需要知道类已加载完成的结果。同样以驱动汽车抵达目的地为例,对于非专业人士而言,是否知道发动机点火的原理和过程并不影响我们通过插入钥匙启动汽车这一结果。类加载器的具体实现可以参考后面的介绍。

2.1.2 类的生命周期

.class文件需要加载到虚拟机中之后才能运行和使用,系统加载.class文件大体上分为三个部分:

  • 加载
  • 连接
  • 初始化

其中,连接又可细化为三个部分:

  • 验证
  • 准备
  • 解析

加载完成后到生命周期结束大致分为如下部分:

  • 使用
  • 卸载

图片说明

一般来说,类加载的整个流程具有线性特征,但是在解析阶段未必会遵守这一特性。解析可以在初始化开始后再执行,这种延迟解析的方式为动态绑定提供了大大的支持。下面将对图中涉及的过程一一介绍。

2.1.2.1 加载

“加载”是类加载过程的第一步,完成后实现以下动作:

  • 通过类的全限定名类文件存储的的二进制字节流;
  • 将二进制字节流所存储的静态结构加载到方法区中作为运行时结构;
  • 在内存中生成一个Class对象作为静态结构的代理。

其中二进制字节流不局限于通过本地IO进行加载,还可以通过其他方式获取:

  • 从压缩包中读取,例如JAR、EAR、WAR等格式;
  • 通过网络获取;
  • 使用反射技术在运行时计算生成;
  • 从DB中加载获取(场景较少)

一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段。此类加载不仅可以使用JVM内置的类加载器还可以通过重写loadClass()方法实现自定义加载。数组类的加载过程则稍显不同,应为数组本身不通过类加载器创建,但是数组元素类型的加载依旧依赖于类加载器。数组的加载过程感兴趣的同学可以自行查阅资料扩展学习,例如深入理解Java虚拟机7.3.1节。

2.1.2.2 连接

连接阶段分为三个子阶段:

  • 验证
  • 准备
  • 解析

2.1.2.2.1 验证

加载实现了对.class文件中二进制流的加载,但是.class文件也可以由其他语言编译产生。此外,二进制流如果不满足Java规范,可能导致出现越界访问等异常。为了解决上述不安全性,JVM在连接阶段假定二进制流不可信,通过一定规则验证二进制流是否满足约束性,避免出现运行期间影响虚拟机正常运行的不利因素。一般来说,验证阶段主要对.class文件中二进制流的结构进行校验,判断其是否符合Class文件规范。主要检查方向包含文件格式验证,元数据验证,字节码验证和符号引用验证。

文件格式验证:验证字节流是否符合Class文件格式的规范,字节流版本是否与本机JVM版本冲突。下面列出一些主要校验的方向:

  • 是否以0xCAFEBABE开头;
  • 本地虚拟机是否可处理Class文件中的主次版本号;
  • 常量池中是否有不可识别的常量类型;
  • 指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量;

.class文件的魔数是0xCAFEBABE,通过在.class文件头部使用该数标识文件类型。其他文件类型例如pdf、png、jpg等具有自己定义的魔数用于程序识别文件类型。

验证阶段结束后可保证二进制流代表的静态结构可被识别转化为方法区中的运行时结构。在该阶段后面的验证阶段都是基于方法区的存储结构进行的,不再与字节流打交道。

元数据验证:元数据可以理解为描述数据的数据,这一阶段主要对字节码的语义展开分析,如果一个类(除java.lang.Object)没有父类,肯定无法通过验证。此外还有一些特殊情况需要验证:

  • 在继承体系上的要求,比如不能继承被final修饰的类(String类就不可被继承);
  • 在接口体系上的要求,比如实现接口的类是否全部实现接口中定义的方法;
  • 在字段/方法和继承体系上的要求,比如子类是否存在非法访

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试必问JVM考点精讲 文章被收录于专栏

“挨踢”行业行情日益严峻,企业招聘的门槛也随之越来越高,大厂hc少之又少。 庞大的知识体系下,不知道学什么、怎么学? 面试高频考点是什么、怎么回答才能得到面试官的青睐? 作为后端求职者,在Java的道路上越走越宽。 本专刊则针对Java面试考点上,精讲JVM知识点,为大家的大厂求职路保驾护航! 针对如今校招痛点,深入详解JVM知识考点,列出高频真题并详细解答!探索JVM精髓!

全部评论
但是在初始化阶段并不执行这一赋值过程,而是赋一个默认的0作为初始值。 这句话中初始化应该换成 准备 吧?
点赞 回复 分享
发布于 2021-07-24 22:35
这里有个问题,在加载阶段已经将二进制流转换成数据结构存储在方法区中了,为什么后面一步才是验证,不应该在存储在方法去之前验证二进制流是否安全吗?
点赞 回复 分享
发布于 2021-07-24 22:03
https://blog.csdn.net/javazejian/article/details/70768369?utm_source=gold_browser_extension 这个博客对class对象讲得很清楚
点赞 回复 分享
发布于 2021-07-24 18:42

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务