Java虚拟机

JVM相关知识

Java的跨平台性

在这里插入图片描述
Java中的跨平台的特征,指的是Java程序能够实现跨平台,而不是JVM,JVM是用C/C++开发的,是编译后的机器码,是不能跨平台,不同的平台需要安装不同版本的JVM
Java程序经过编译可以在任何平台上执行,仅仅需要JVM这个中间层,来做不同平台的环境差异兼容,可以让Java程序在不同平台上运行,真正实现“一次编译,到处运行”的目的。

JVM概念

JVM即Java Virtual Machine,Java虚拟机
JVM是Java的核心和基础,在Java编译器和操作系统平台之间的虚拟处理器,利用软件来实现的抽象的计算机,基于下层操作系统和硬件平台,可以在JVM上执行Java的字节码程序
JVM包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法区域,JVM屏蔽了与具体操作系统平台相关的信息,是Java程序只需要生成JVM虚拟机上运行的目标代码,就可以在多个平台上进行执行

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工作原理

Java代码如何运行

在这里插入图片描述

工作过程介绍

在这里插入图片描述
JVM系统被划分了三个主要的子系统:
类装载子系统(Class Loader Sunsystem)
运行时数据区域(Runtime Data Areas)
执行引擎(Execution Engine)

类装载子系统
Java的动态类加载功能由类装载子系统来实现,可以加载、链接、第一次运行引用类时初始化类文件

  • 加载:功能是加载类,共有三种类加载器BootStrap ClassLoader、Extension ClassLoader和Application ClassLoader
  • 链接
  • 验证:字节码验证器将验证生成字节码是否正确,如果验证失败,将无法继续后续操作
  • 准备:对于所有的静态变量,内存将被分配默认值
  • 解析:将所有的符号内存引用都替换为来自方法区域的原始引用
  • 初始化:静态变量都将被赋予原始值,静态代码块将被执行

运行时数据区域

  • 方法区:类级别数据、静态变量存储位置。线程共享
  • 堆区:对象及其对象实例变量和数组存储位置。线程共享
  • 虚拟机栈:线程私有的
  • 本地方法栈:保存本的方法的信息(JNI),线程私有
  • 程序计数器:线程私有的PC寄存器,在执行指令时,保存当前指令地址

执行引擎
分配给运行时数据区域的字节码将有执行引擎执行,执行引擎读取字节码并逐个执行

  • 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
    它是执行引擎所需的本地库的集合。

类加载机制

类加载时机

虚拟机规范严格规定了只有5种情况必须对类进行“初始化”(class文件加载到JVM中)

  • 创建对象实例:new对象的时候,会对类的初始化,前提这个类没有被初始化
  • 调用类的静态属性或者静态属性赋值
  • 调用类的静态方法
  • 通过class文件反射创建对象
  • 初始化一个类的子类,使用子类的时候先初始化父类
  • JVM启动时被标记为启动的类,比如main方法所在的类
    Java类的加载是动态,并不会一次性将所有的类全部加载再执行,而是保证程序运行的基础类完全加载到JVM中,至于其它类,则在需要的时候才会加载,目的是为了节省内存开销

不会进行初始化的情况:

  • 在同一个类加载器下面只能被初始化类一次,如果已经初始化了就不必再初始化了
  • 在编译的时候能确定下来的静态变量,不会对类进行初始化,比如final修饰静态变量
类加载器

负责将字节码文件(class)加载到内存中即运行时数据区域

类加载器介绍
在这里插入图片描述
JVM中内置了三个重要的类加载器,除了BootStrap ClassLoader其他类加载器均有Java实现并全部继承自Java.lang.ClassLoader
各个加载器的职责:

  • BootStrap ClassLoader:负责加载jre/lib/rt.jar里所有的class或者被-Xbootclasspath参数指定路径中的所有类,由C++实现,不是ClassLoader的子类
  • Extension ClassLoader:负责加载Java平台中扩展的一些jar包,包括jre/lib/ext下的jar包或者是-Djava.ext.dirs指定目录下的jar
  • Application ClassLoader:面向用户的加载器,负责加载当前应用的class path下的所有jar包和类
JVM类加载的方式

类加载方式有三种方式:

  • 命令行启动应用时候由JVM初始化加载
  • 通过Class.forName()方法动态加载
  • 通过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()方法才用调用构造函数,创建类的对象
双亲委派模型

在这里插入图片描述
双亲委派模型工作过程:

  • 1、当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。
  • 2、如果没有找到,就去委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托其父类去加载,直到委托到启动类加载器为止。因为如果父类加载器为空了,就代表使用启动类加载器作为父加载器去加载该类。(也就是看到的String类加载器为null)
  • 3、如果启动类加载器加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用AppClassLoader来加载,继续失败就会抛出一个异常ClassNotFoundException

双亲委派模型好处:

  • 安全性:避免用户自己编写的类动态替换Java的一些核心类
  • 避免类的重复加载
类加载的详细过程

在这里插入图片描述
加载、验证、准备、初始化、卸载这五个阶段过程是固定的,在类加载过程中必须按照这种顺序按部就班进行,而解析阶段则不一样,在某种情况下可以在初始化后完成

加载
加载阶段,虚拟机要完成三件事:

  • 通过一个类的全限定名获取定义此类的二进制字节流。
  • 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构。
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据结构的访问入口。加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。

连接一般是加载阶段和初始化阶段交叉进行,过程由以下三部分组成:
验证
确定该类是否符合java语言的规范,有没有属性和行为的重复,继承是否合理,总之,就是保证jvm能够执行验证阶段主要完成下面4个阶段的校验动作:

  • 文件格式验证︰验证字节流是否符合Class文件格式的规范;例如︰是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
  • 元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如︰这个类是否有父类,除了java.lang.Object之外。
  • 字节码验证︰通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:确保解析动作能正确执行。

准备
准备阶段主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值
默认初始值如下∶

  • 八种基本数据类型默认的初始值是0
  • 引用类型默认的初始值是null
  • 有static final修饰的会直接赋值,例如: static final int x=10;则默认就是10.

解析
解析的任务就是把常量池中的符号引用转换为直接引用,即jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。
解析阶段是将虚拟机常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号进行.
符号引用:简单的理解就是字符串,比如引用一个类, java.util.ArrayList 这就是一个符号引用,字符串引用的对象不一定被加载。
直接引用:指针或者地址偏移量。引用对象一定在内存(已经加载)。

初始化
初始化这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化,执行的顺序就是︰父类静态域或者静态代码块,然后是子类静态域或者子类静态代码块(静态代码块先被加载,然后再是静态属性)类初始化是类加载的最后一步,除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。到了初始化阶段才真正执行Java代码。
类的初始化的主要工作是为静态变量赋程序设定的初值
如static int a = 100;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为100。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值