本篇内容包括:Java 类的加载机制(Jvm 结构组成、Java 类的加载)、类的生命周期(加载-验证-准备-解析-初始化-使用-卸载)、类加载器 以及 双亲委派模型。

一、Java 类的加载机制

1、 Jvm 结构组成

Jvm 整体组成可分为四个部分:类加载器、运行时数据区(Runtime Data Area)、执行引擎(Execution Engine)、本地库接口(Native Interface)

  • 类加载器:负责从字节码(Class)文件中,加载 class 信息到运行时数据区的方法区;
  • 运行时数据区:存放 Jvm 在执行 Java 程序时相关数据的区域;
  • 执行引擎:将字节码翻译成底层系统指令再交由 CPU 去执行;
  • 本地库接口:执行过程中可能需要调用到其他语言(比如 C 语言)的本地接口。

PS:Javac 是收录于 Jdk 中的 Java 语言编译器。该工具可以将后缀名为 .java 的源文件编译为后缀名为 .class 的可以运行于 Java 虚拟机的字节码。

程序在被执行之前, Java 代码会被先转换成字节码(.class 文件), Jvm 首先通过一定的方式类加载器(ClassLoader)把字节码文件加载到内存中运行时数据区(Runtime Data Area),而字节码文件是 Jvm 提供的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface)来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能。

而我们通常所说的 Jvm 组成指的是运行时数据区,因为通常需要程序员调试分析的区域就是运行时数据区,或者更具体的来说就是运行时数据区里面的堆(Heap)模块!

2、Java 类的加载

类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class 对象,Class 对象封装了类在方法区内的数据结构,并且向 Java 程序员提供了访问方法区内的数据结构的接口。

类加载器并不需要等到某个类被首次主动使用时再加载它, Jvm 规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了 .class 文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误( LinkageError 错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

加载.class 文件的方式:

  • 从本地系统中直接加载
  • 通过网络下载 .class 文件
  • 从 zip、jar 等归档文件中加载 .class 文件
  • 从专有数据库中提取 .class 文件
  • 将 Java 源文件动态编译为 .class 文件
  • 由其他文件生成

二、Java 类的生命周期

类从被加载到 Jvm 内存中开始到卸载出内存为止,生命周期分为7个阶段:加载-验证-准备-解析-初始化-使用-卸载。(或分为5个阶段,把 验证-准备-解析 分为连接阶段)

1、加载

加载过程就是把 class 字节码文件载入到虚拟机中,至于从哪儿加载,虚拟机设计者并没有限定,你可以从文件、压缩包、网络、数据库等等地方加载 class 字节码。

  • 通过一个类的全限定名来获取其定义的二进制字节流;
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  • 在Java堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口

2、验证(连接阶段的第一步):确保被加载的类的正确性

这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用 -Xverifynone 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

3、准备(连接阶段的第二步):为类的静态变量分配内存,并将其初始化为默认值

准备阶段的工作就是为类的静态变量分配内存并设为 Jvm 默认的初值,对于非静态的变量,则不会为它们分配内存。静态变量的初值为 Jvm 默认的初值,而不是我们在程序中设定的初值。(仅包含类变,不包含实例变量)

4、解析(连接阶段的第三步):把类中的符号引用转换为直接引用

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

5、初始化:为类的静态变量赋予正确的初始值

主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  1. 声明类变量是指定初始值;
  2. 使用静态代码块为类变量指定初始值

Jvm初始化步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类

  2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类

  3. 假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

  1. 创建类的实例,也就是new的方式
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(如 Class.forName(“com.shengsiyuan.Test”) )
  5. 初始化某个类的子类,则其父类也会被初始化
  6. Java虚拟机启动时被标明为启动类的类( JavaTest ),直接使用 java.exe 命令来运行某个主类

三、类加载器

类加载器是负责将可能是网络上、也可能是磁盘上的 .class 文件加载到内存中。并为其生成对应的 java.lang.Class 对象。一旦一个类被载入 Jvm 了,同一个类就不会被再次加载。那么怎样才算是同一个类?在 Java 中一个类用其全限定类名(包名和类名)作为其唯一标识,但是在 Jvm 中,一个类用其全限定类名和其类加载器作为其唯一标识。也就是说,在 Java 中的同一个类,如果用不同的类加载器加载,则生成的 .class 对象认为是不同的。

当 Jvm启动时,会形成由三个类加载器组成的初始类加载器层次结构:

  • 启动类加载器(Bootstrap ClassLoader):是嵌在 Jvm 内核中的加载器,该加载器是用 C++ 语言写的,主要负载加载 JAVA_HOME/lib 下的类库,启动类加载器无法被应用程序直接使用;
  • 扩展类加载器(Extension ClassLoader):该加载器器是用JAVA编写,且它的父类加载器是 Bootstrap,是由 sun.misc.Launcher$ExtClassLoader实现的,主要加载 JAVA_HOME/lib/ext 目录中的类库。开发者可以这几使用扩展类加载器;
  • 系统类加载器(App ClassLoader):也称为应用程序类加载器,负责加载应用程序 classpath 目录下的所有 .jar 和 .class 文件。它的父加载器为 Extension ClassLoader。

上述三种类加载器的层次关系如下:

Ps:类加载器的体系并不是“继承”体系,而是委派体系,大多数类加载器首先会到自己的 parent 中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。

五、双亲委派模型

1、双亲委派模型

如果以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器(继承 java.lang.ClassLoader 类)

它们的层级关系即 自定义类加载器 -> 应用程序加载器 -> 扩展加载器 -> 启动类加载器,这种层次关系被称作为双亲委派模型:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。

2、双亲委派模式优势

  • 采用双亲委派模式的是好处是 Java 类随着它的类加载器一起具备了一种带有先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次;
  • 其次是考虑到安全因素,Java 核心 api 中定义类型不会被随意替换,假设通过网络传递一个名为 java.lang.Integer 的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心 Java API 发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的 java.lang.Integer,而直接返回已加载过的 Integer.class,这样便可以防止核心 API 库被随意篡改;
  • 可能你会想,如果我们在 classpath 路径下自定义一个名为 java.lang.SingleInterge 类呢?该类并不存在 java.lang 中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为 java.lang 是核心 API 包,需要访问权限,强制加载将会报出异常 java.lang.SecurityException: Prohibited package name: java.lang 所以无论如何都无法加载成功的。

深入理解Java虚拟机:Java类的加载机制相关推荐

  1. 深入java虚拟机学习 -- 类的加载机制(续)

    昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...

  2. Java虚拟机中 类的加载过程

    Java中 类的加载过程 例如下面的一段简单的代码 public class HelloWorld {public static void main(String[] args) {System.ou ...

  3. Java虚拟机 —— 类的加载机制

    我们知道class文件中存储了类的描述信息和各种细节的数据,在运行Java程序时,虚拟机需要先将类的这些数据加载到内存中,并经过校验.转换.解析和初始化过后,最终形成可以直接使用的Java类型. 类从 ...

  4. 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器

    28Java类的加载机制 28.1.什么是类的加载 28.2.类的生命周期 28.2.1.加载:查找并加载类的二进制数据 28.2.2.连接 28.2.3.初始化 28.3.类加载器 28.4.类的加 ...

  5. java类验证和装载顺序_Java类的加载机制和双亲委派模型

    Java类的加载机制和双亲委派模型 1类的加载机制 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading).验证(Verification).准备(Prepar ...

  6. java 根据类名示例化类_如何使用示例从Java中的类路径加载资源

    java 根据类名示例化类 Java中的类路径不仅用于加载.class文件,而且还可以用于加载资源,例如属性文件,图像,图标,缩略图或任何二进制内容. Java提供了API来将这些资源读取为Input ...

  7. 如何使用示例从Java中的类路径加载资源

    Java中的类路径不仅用于加载.class文件,而且还可以用于加载资源,例如属性文件,图像,图标,缩略图或任何二进制内容. Java提供了API来将这些资源读取为InputStream或URL. 假设 ...

  8. Java内部类以及类的加载顺序(一)

    java内部类以及类的加载顺序(一) Java 类中不仅可以定义变量和方法,还可以定义类,这样定义在类内部的类就被称为内部类.根据定义的方式不同,内部类分为四大类静态内部类,成员内部类,局部内部类,匿 ...

  9. JVM-01:类的加载机制

    本文从 纯洁的微笑的博客 转载 原地址:http://www.ityouknow.com/jvm.html 类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内 ...

最新文章

  1. 如何在小型pcb的移动设备上获得更好的无线性能
  2. 难离难舍的internet2与SDN
  3. 专访王田苗:机器人是“刚需”,市场正处于逆周期增长
  4. jQuery样式操作
  5. Python 传值和传址 copy/deepcopy
  6. 解题报告——蓝桥杯 试题 基础练习 2n皇后问题(附n皇后代码)
  7. 【译】LiveData 在 SnackBar/Navigation 情景下的使用(SingleLiveEvent)
  8. 2018年下半年网络公式考试案例分析真题
  9. VBS使文本框的光标位于所有字符后
  10. 查看PLC IP 端口_详解S7-1500的以太网通信数据类型:TCON_IP_v4
  11. fork函数结果分析
  12. SetWindowsHookEx全局钩子
  13. 找到某个关键字 同义词词林 python_读一读我——无废话Python(三)条件、循环、函数、表达式
  14. Toast基本使用方法
  15. 开发3D游戏建模都需要哪些软件?软件繁多,如何从中挑选学习?
  16. UE4 渲染半透明物体进深度剔除(复制物体)
  17. 【Python画图】不显示x、y坐标
  18. ANDROID下面的游戏更新目录
  19. 挂号信经过几次签收最终送达收件人?
  20. Raspberry/Ubuntu 20.04命令行连接eap-gtc企业wifi

热门文章

  1. 什么是商业型产品经理?需要具备哪些能力和知识素养?
  2. java 解密 sha,如何在Java中解密sha1加密的字符串
  3. vrrp广播风暴_VRRP常见故障解决方法
  4. oracle主键约束删除,oracle删除主键查看主键约束及创建联合主键
  5. 计算机专业的大学计划书,大学学习计划书
  6. Java学习总结:飘逸的字符串
  7. Python中关于反斜杠(\)用法的总结
  8. 陕西师范大学计算机类专业咋样,陕西师范大学计算机科学学院
  9. 驾驭和取悦-《后宫甄嬛传》看后感
  10. java etl 开源框架_开源的ETL工具