⭐写在前面⭐
📢今天我们进行 Java JVM基础知识 的学习,感谢你的阅读,内容若有不当之处,希望大家多多指正,一起进步💯!!!
♨️如果觉得博主文章还不错,可以👍三连支持⭐一下哦😀
文章目录
🍀一、JVM基础知识
☘️1. Java的跨平台性
Java中的跨平台特征,指的是Java程序能够实现跨平台,而不是JVM,JVM是用C/C++开发的,是编译后的机器码,是不能跨平台的,不同的平台需要安装不同版本的JVM。
Java程序经过编译,可以在任何平台上执行,仅仅需要JVM这一个中间层,来做不同平台的环境差异的兼容,可以让Java程序在不同平台的运行,真正实现了“一次编码,到处执行”的目的。
☘️2. JVM概念
- JVM即Java Virtual Machine,Java虚拟机。
- JVM是Java的核心和基础,在Java编译器和操作系统之间的虚拟处理器,利用软件方法来实现的抽象的计算机,基于下层操作系统和硬件平台,可以在JVM上执行Java的字节码程序。
- JVM包括
一套字节码指令集
,一组寄存器
,一个栈
,一个垃圾回收堆
和一个存储方法区域
,JVM屏蔽了具体操作系统平台相关的信息,是Java程序只需要生成JVM虚拟机上运行的目标代码,就可以在多个平台上执行。
☘️3. JRE/JDK/JVM关系
- JVM(Java virtual Machine Java虚拟机)是JRE的一部分,是一个虚拟出来的计算机,是通过在实际的计算机上仿真模拟实现各种计算机功能来实现的。
- JRE(Java Runtime Environment Java运行环境)也就是Java平台,所有的Java程序都要在JRE下才能运行,普通用户只需要运行已开发好的Java 程序,安装JRE即可。
- JDK(Java Development Kit Java开发工具包),是程序开发者用来进行编译,调试Java程序要用到的开发工具包,JDK的工具也是Java程序,也需要在JRE上才能运行。为了保持JDK的完整性和独立性,在JDK安装过程中,JRE也是安装的一部分,所有在JDK的安装目录下有一个jre目录,用户存放jre文件。
🍀二、JVM生命周期
- JVM是负责运行一个Java程序,当启动一个Java程序时,一个虚拟机实例也就诞生了,当程序关闭退出时,虚拟机实例也就随之消亡。
- JVM实例通过调用某个初始类的main方法来运行一个Java程序而这个main方法必须是公有的(public)、静态的(static)、返回值为void、并且接收一个字符串数组作为参数,任何拥有这个main方法的都可以作为Java程序的起点。
🍀三、JVM工作过程
☘️1. Java代码如何运行
☘️2. 工作过程介绍
JVM系统被划分了主要三个子系统
- 类装载子系统(Class Loader Subsystem)
- 运行时数据区域(Runtime Data Areas)
- 执行引擎(Execution Engine)
🌵(1) 类装载子系统
Java的动态类加载功能由类装载子系统来实现,可以加载、链接、第一次运行引用类时初始化类文件。
- 加载:功能是加载类,共有三种类加载器
BootStrap ClassLoader
、Extension ClassLoader
和Application ClassLoader
。 - 链接:链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中,经由验证、准备和解析三个阶段。
– 验证:字节码验证器将验证生成字节码是否正确,如果验证失败,将无法继续后续操作
– 准备:对于所有的静态变量,内存将被分配默认值
– 解析:将所有的符号内存引用都替换为来自方法区域的原始引用 - 初始化:静态变量都将被赋予原始值,静态代码块将被执行。
🌵(2)运行时数据区域
- 方法区:类级别数据、静态变量存储位置。线程共享
- 堆区:对象及其对象实例变量和数组存储位置。线程共享
- 虚拟机栈:线程私有的
- 本地方法栈:保存本地方法的信息(JNI),线程私有
- 程序计数器:线程私有的PC寄存器,在执行指令时,保存当前指令地址
🌵(3)执行引擎
分配给运行时数据区域的字节码将由执行引擎执行,执行引擎读取字节码并逐个执行
● Interpreter
解释器解释字节码很快,但执行缓慢。解释器的缺点是,当一个方法被多次调用时,每次都需要新的解释。
● JIT Compiler
JIT编译器消除了解释器的缺点。执行引擎将使用解释器的帮助来转换字节代码,但是当它发现重复的代码时,它使用JIT编译器,它编译整个字节码并将其更改为本机代码。这种本机代码将直接用于重复的方法调用,从而提高系统的性能。
● Intermediate Code generator
生成中间代码。
● Code Optimizer
负责优化上面生成的中间代码。
● Target Code Generator
负责生成机器代码或本机代码。
● Profiler
一种特殊的组件,负责查找hotspots,即该方法是否被多次调用。
● 垃圾回收
收集和删除未引用的对象。可以通过调用“System.gc()”来触发垃圾收集,但不会立即执行(执行时机由GC决定)。JVM的垃圾回收收集已创建的对象。
● Java Native Interface (JNI)
JNI将与本地的方法库交互并为执行引擎提供所需的本地库。
● Native Method Libraries
它是执行引擎所需的本地库的集合。
🍀四、类加载机制
☘️1. 类加载时机
虚拟机规范严格规定了只有6种情况必须立即对类进行“初始化”(class文件加载到JVM中)
● 创建对象实例:new对象的时候,会对类的初始化,前提这个类没有被初始化
● 调用类的静态属性或者对静态属性赋值
● 调用类的静态方法
● 通过class文件反射创建对象
● 初始化一个类的子类,使用子类时候先初始化父类
● JVM启动时被标记为启动的类,比如main方法所在类
🍁注:Java类的加载是动态的,并不会一次性将所有的类全部加载再执行,而是保证程序运行的基础类完全加载到JVM中,至于其他类,则在需要的时候才会加载,目的是为难了节省内存开销。
不会进行初始化的情况:
● 在同一个类加载器下面只能被初始化类一次,如果已经初始化了就不必在初始化了
● 在编译的时候能确定下来的静态变量,不会对类进行初始化,比如final修饰静态变量
☘️2. 类加载器
负责将字节码文件(class)加载到内存中即运行时数据区域
🌵(1)类加载器介绍
JVM中内置了三个重要的类加载器,除了
BootStrapClassLoader
类加载器外,其他类加载器均由Java实现并全部继承自Java.lang.ClassLoader
各个加载器的职责:
- BootStrapClassLoader
负责加载jre/lib/rt.jar 里所有的class或者被-Xbootclasspath参数指定路径中的所有类,由C++实现,不是ClassLoader的子类。 - Extension ClassLoader
负责加载Java平台中扩展的一些jar包,包括jre/lib/ext下的jar包或者是-Djava.ext.dirs指定目录下的jar。 - Application ClassLoader
面向我们用户的加载器,负责加载当前应用的classpath下的所有jar包和类
🌵(2)JVM类加载的方式
类加载有三种方式:
● 1、命令行启动应用的时候由JVM初始化加载
● 2、通过Class.forName()方法动态加载
● 3、通过ClassLoader.loadClass()方法动态加载
Class.forName()和ClassLoader.loadClass()区别
● Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;
● ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
● Class.forName(name,initialize,loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象 。
🌵(3)双亲委派模型
双亲委派模型的工作过程如下:
1、当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。
2、如果没有找到,就去委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托其父类去加载,直到委托到启动类加载器为止。因为如果父类加载器为空了,就代表使用启动类加载器作为父加载器去加载该类。(也就是看到的String类加载器为null)
3、如果启动类加载器加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用AppClassLoader来加载,继续失败就会抛出一个异常ClassNotFoundException。
🍁双亲委派模型的好处
- 安全性:避免用户自己编写的类动态替换Java的一些核心类
- 避免类的重复加载
☘️3. 类记载的详细过程
加载
、验证
、准备
、初始化
、卸载
这五个阶段过程是固定的,在类加载的过程中必须按照这种顺序按部就班的进行,而解析阶段则不一样,在某种情况下可以在初始化后完成。
🌵(1)加载
加载阶段,虚拟机需要完成3件事:
● 通过一个类的全限定名获取定义此类的二进制字节流。
● 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构。
● 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据结构的访问入口。
加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。
🌵(2)验证
确定该类是否符合java语言的规范,有没有属性和行为的重复,继承是否合理,总之,就是保证JVM能够执行。
验证阶段主要完成下面4个阶段的校验动作:
● 文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
● 元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。
● 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
● 符号引用验证:确保解析动作能正确执行。
🌵(3)准备
准备阶段主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值。
默认初始值如下:
- 八种基本数据类型默认的初始值是0
- 引用类型默认的初始值是null
- 有static final修饰的会直接赋值,例如:static final int x=10;则默认就是10
🌵(4)解析
解析的任务就是把常量池中的符号引用转换为直接引用,即JVM会将所有的类或接口名、字段名、方法名转换为具体的内存地址。
解析阶段是将虚拟机常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号进行。
符号引用: 简单的理解就是字符串,比如引用一个类,java.util.ArrayList 这就是一个符号引用,字符串引用的对象不一定被加载。
直接引用: 指针或者地址偏移量。引用对象一定在内存(已经加载)。
🌵(5)初始化
初始化这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化, 执行的顺序就是:父类静态域或着静态代码块,然后是子类静态域或者子类静态代码块(静态代码块先被加载,然后再是静态属性)类初始化是类加载的最后一步,除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。到了初始化阶段才真正执行Java代码。类的初始化的主要工作是为静态变量赋程序设定的初值 。如static int a = 100;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为100。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95494.html