🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇
⭐ JVM ⭐
🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇
今日推荐歌曲: Right Now (Na Na Na) -- Aamir 🎵🎵
目录
1) 加载 加载)
2) 验证 验证)
3) 准备 准备)
4) 解析 解析)
5) 初始化 初始化)
前言
JVM是JavaVirtualMachine的简称,意为Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。
JVM执⾏流程: 程序在执⾏之前先要把java代码转换成字节码(class⽂件),JVM⾸先需要把字节码通过⼀定的⽅式 类加载器(ClassLoader) 把⽂件加载到内存中运⾏时数据区(RuntimeDataArea) ,⽽字节码⽂ 件是JVM的⼀套指令集规范,并不能直接交个底层操作系统去执⾏,因此需要特定的命令解析器**执 ⾏引擎(ExecutionEngine)**将字节码翻译成底层系统指令再交由CPU去执⾏ ,⽽这个过程中需要 调**⽤其他语⾔的接⼝本地库接⼝(NativeInterface)来实现整个程序的功能,**这就是这4个主要组成 部分的职责与功能。
这里详细介绍 JVM 的类加载过程.
①类加载过程
从上⾯的图⽚我们可以看出整个JVM执⾏的流程中,和我们关系最密切的就是类加载的过程了,所 以接下来我们来看下类加载的执⾏流程。 对于⼀个类来说,它的⽣命周期是这样的:
其中前5步是固定的顺序并且也是类加载的过程,其中中间的3步我们都属于连接,所以对于类加载 来说总共分为以下⼏个步骤:
1. 加载
2. 连接
a. 验证
b. 准备
c. 解析
3. 初始化
下⾯我们分别来看每个步骤的具体执⾏内容。
1) 加载
"加载"(Loading)阶段是整个"类加载"(ClassLoading)过程中的⼀个阶段,它和类加载 Class Loading 是不同的,⼀个是加载Loading另⼀个是类加载ClassLoading,所以不要把⼆者搞混 了。
在加载Loading阶段,Java虚拟机需要完成以下三件事情:
1)通过⼀个类的全限定名来获取定义此类的⼆进制字节流。
2)将这个字节流所代表的静态存储结构转化为⽅法区的运⾏时数据结构。
3)在内存中⽣成⼀个代表这个类的 java.lang.Class 对象(类对象),作为⽅法区这个类的各种数据的访问⼊⼝。
加载是类加载过程的第一步,它的主要任务是查找并加载类文件。当程序需要使用某个类时,JVM会通过类加载器查找对应的类文件,通常是从磁盘或网络加载类文件(认为读的是二进制的)。
2) 验证
验证是连接阶段的第⼀步,这⼀阶段的⽬的是确保Class⽂件的字节流中包含的信息符合《Java虚拟机 规范》的全部约束要求,保证这些信息被当作代码运⾏后不会危害虚拟机⾃⾝的安全。
验证选项:
• ⽂件格式验证
• 字节码验证
• 符号引⽤验证...
确保类文件遵循Java虚拟机规范,不会造成安全问题。
3) 准备
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值 的阶段。
⽐如此时有这样⼀⾏代码:
public static int value = 123;
它是初始化value的int值为0,⽽⾮123。
为类的静态变量分配内存空间并初始化为默认值。
此时申请到的内存空间,里面的默认值,都是全0的.
(这个阶段中,类对象里的静态成员变量的值也就相当于是0了)
4) 解析
解析阶段是Java虚拟机将常量池内的**符号引⽤替换为直接引⽤**的过程,也就是初始化常量的过程。
将类、方法、字段的符号引用解析为直接引用。
那么就有个问题了 , 什么是符号引用 , 什么是直接引用呢 ?
符号引用是一种符号性的引用,描述了类、方法、字段在编译时的名称,但并不直接指向内存位置。而直接引用则是对内存位置的直接指向,可以直接使用。在解析阶段,符号引用会被解析为直接指向内存位置的引用,这样程序就能正确地访问类、方法和字段,从而执行相应的操作。
举例来说,当一个类引用另一个类的方法时,编译器生成的引用是一个符号引用,指向要调用的方法的名称。在类加载的解析阶段,这个符号引用会被解析为指向具体方法内存位置的直接引用,这样在运行时就可以直接调用方法。
这种符号引用替换为直接引用的过程是为了确保程序在运行时能够准确地访问和执行类、方法和字段,保证程序的正常运行。
5) 初始化
初始化阶段,Java虚拟机真正开始执⾏类中编写的Java程序代码,将主导权移交给应⽤程序。初始 化阶段就是执⾏类构造器⽅法的过程。
初始化是类加载的最后一个阶段,主要处理静态变量的赋值和执行静态代码块。在初始化阶段,类的静态变量被赋予初始值,静态代码块被执行。
②双亲委派模型
提到类加载机制,不得不提的⼀个概念就是"双亲委派模型"。
站在Java虚拟机的⻆度来看,只存在两种不同的类加载器:
⼀种是启动类加载器(Bootstrap ClassLoader),这个类加载器使⽤C++语⾔实现,是虚拟机⾃⾝的⼀部分;
另外⼀种就是其他所有的 类加载器,这些类加载器都由Java语⾔实现,独⽴存在于虚拟机外部,并且全都继承⾃抽象类 java.lang.ClassLoader。 站在Java开发⼈员的⻆度来看,类加载器就应当划分得更细致⼀些。
⾃JDK1.2以来,Java⼀直保 持着三层类加载器、双亲委派的类加载架构器。
什么是双亲委派模型?
如果⼀个类加载器收到了类加载的请求,它⾸先不会⾃⼰去尝试加载这个类,⽽是把这个请求委派给 ⽗类加载器去完成,每⼀个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层 的启动类加载器中,只有当⽗加载器反馈⾃⼰⽆法完成这个加载请求(它的搜索范围中没有找到所需 的类)时,⼦加载器才会尝试⾃⼰去完成加载。
具体而言,双亲委派模型遵循以下原则:
- 当一个类加载器收到加载一个类的请求时,它首先会检查是否已经加载这个类。如果已经加载过,就直接返回该类。
- 如果尚未加载过该类,类加载器会委托给其父加载器进行加载。
- 如果父加载器无法加载该类,子加载器才会尝试加载。子加载器首先尝试加载,如果加载失败则委托给父加载器。
- 如果所有的父加载器都无法加载该类,最后由当前类加载器自行加载。
这种机制有助于确保类的唯一性,避免同一个类被多个类加载器加载多次,导致类的重复以及类的不一致性问题。通过双亲委派模型,Java的类加载器可以形成一个层次结构,自底向上逐级加载类,保证了类的一致性和安全性。
双亲委派模型的优点
- 避免重复加载类:⽐如A类和B类都有⼀个⽗类C类,那么当A启动时就会将C类加载起来,那 么在B类进⾏加载时就不需要在重复加载C类了。
- 安全性:使⽤双亲委派模型也可以保证了Java的核⼼API不被篡改,如果没有使⽤双亲委派模 型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个称为java.lang.Object 类的话,那么程序运⾏的时候,系统就会出现多个不同的Object类,⽽有些Object类⼜是⽤⼾⾃ ⼰提供的因此安全性就不能得到保证了。
③破坏双亲委派模型
破坏双亲委派模型可能会导致一些潜在的安全性和类加载冲突问题。虽然双亲委派模型的设计有助于维护类加载的有序性和安全性,但有时候会出现需要破坏该模型的情况,比如:
特殊的类加载需求:在某些特定场景下,可能需要定制类加载顺序或实现动态加载机制,这就需要破坏双亲委派模型。
类加载器隔离:某些框架或应用需要以不同的类加载器加载不同版本的同一个类,为了避免冲突和实现隔离,可能需要打破双亲委派模型。
热部署:在一些开发环境或容器中,可能需要进行热部署,即在应用程序运行时动态替换和加载类文件,这可能需要通过自定义类加载器来实现,进而破坏双亲委派模型。
动态生成类:某些应用场景需要在运行时动态创建和加载类,这也可能需要破坏双亲委派模型来实现。
虽然在特定情况下破坏双亲委派模型可能是必要的,但这也会增加程序的复杂性和潜在的安全风险。因此,如果要破坏双亲委派模型,需要充分了解相关原理和注意事项,并尽量避免破坏模型带来的潜在问题。
总结
类加载是Java虚拟机(JVM)在运行时加载类文件并将其转换为Java程序可以使用的对象的过程。类加载过程可以分为以下步骤:
-
加载(Loading):
- 加载是类加载过程的第一步,它的主要任务是查找并加载类文件。当程序需要使用某个类时,JVM会通过类加载器查找对应的类文件,通常是从磁盘或网络加载类文件。
-
链接(Linking):
- 链接是类加载的第二个阶段,包括三部分:验证(Verification)、准备(Preparation)、解析(Resolution)。
- 验证:确保类文件遵循Java虚拟机规范,不会造成安全问题。
- 准备:为类的静态变量分配内存空间并初始化为默认值。
- 解析:将类、方法、字段的符号引用解析为直接引用。
- 链接是类加载的第二个阶段,包括三部分:验证(Verification)、准备(Preparation)、解析(Resolution)。
-
初始化(Initialization):
- 初始化是类加载的最后一个阶段,主要处理静态变量的赋值和执行静态代码块。在初始化阶段,类的静态变量被赋予初始值,静态代码块被执行。
总的来说,类加载过程是JVM加载、验证、准备、解析和初始化类文件的过程,确保类文件在运行时能够被正确加载和处理。
双亲委派模型参考上述。
今天的笔记到这,后续会有更多关于JVM 的精彩内容,这里剧透一波,JVM回收机制和JVM运⾏时数据区
博客不易,希望可以帮助到大伙,动动小手点个赞我会开心很久,感谢阅览。