JVM底层

面试-JVM

JDK、JRE与JVM之间的关系

  • JDK:Java的SDK开发工具包

  • JRE:运行时环境;包含JavaAPI、JVM在内,提供了Java程序执行的最低的环境要求

  • JVM:为Java字节码的执行提供了保障

类加载子系统—>执行引擎子系统—>运行时数据区—>垃圾回收子系统—>本地接口和本地方法库

面试-类加载子系统

类加载子系统将这些字节码文件先装载进内存

类加载:JVM需要用到某个类;虚拟机会加载它的.class文件创建对应的Class对象

类加载过程

  • 加载

    • 通过一个类的全限定名来获取定义此类的二进制流

    • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构

    • 内存中生成一个.class对象,作为方法区这个类的各种数据访问接口

  • 验证

    • 文件格式验证

      • 是否以魔数0xCAFEBABE开头

      • 主、次版本号是否在当前Java虚拟机接受范围之内

      • 常量池是否有不支持的常亮、是否有不存在的常量

      • UTF8编码

      • Class文件中信息

    • 元数据验证

      • 是否继承了不允许继承的类

      • 非抽象类是否实现了其他类或者接口之中要实现的所有方法

      • 字段方法是否与父类产生矛盾

    • 字节码验证

      • 通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑

    • 符号引用验证

      • 该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源

  • 准备;解析

    • 静态变量分配内存空间

    • 类方法解析

    • 接口方法解析

  • 初始化

    • 为静态变量赋值

    • 执行static代码块

    • 执行类构造器方法

  • 使用和卸载

    • 虚拟机自带的类加载器加载的类,在虚拟机的生命周期中始终不会被卸载

类加载器顺序

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类

  • Bootstrap ClassLoader

    加载$JAVA_HOME中jre/lib/rt.jar里所有的class

  • Extension ClassLoader

    加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

  • App ClassLoader

    加载classpath中指定的jar包及目录中class

  • Custom ClassLoader

    应用程序根据自身需要自定义的ClassLoader

  • 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载ClassLoaderId类加载器 + PackageName包名 + ClassName类名称】,如果已经加载则直接返回原来已经加载的类

  • 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader

  • 所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回

双亲委托模型

  • 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载ClassLoaderId类加载器 + PackageName包名 + ClassName类名称】,如果已经加载则直接返回原来已经加载的类

  • 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader

  • 所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回

为何采用双亲委派机制

  • 防止类的重复加载

  • 保护程序安全、防止核心API被篡改

热部署

  • 销毁该自定义ClassLoader

  • 更新class类文件

  • 创建新的ClassLoader去加载更新后的class类文件

为什么要破坏双亲委托

  • 实现可拔插机制mysql的Driver接口定义在jdk当中的,而其实现由各个数据库的服务商来提供启动类加载器来委托子类来加载Driver实现】

  • 加载流程

    • Bootstrap加载Java核心类(核心类中包含Launcher类)

    • 初始化并创建Ext、App类加载器

    • 将Ext设置为App的父类加载器 → 同时再将App设置为默认的线程上下文类加载器

    • Bootstrap加载接口的时候委托子类 App ClassLoader依赖SPI的动态服务发现机制加载META-INF/services路径下的实现类;

面试-执行引擎子系统

字节码指令解释/编译成对应平台上的本地机器指令

执行技术

  • 解释执行:用到某处代码时,转换为机器码执行

  • 静态编译:程序在启动前,编译成对应平台的机器码

  • 即时编译:运行过程中,执行比较频繁的代码转换机械码并存储下来

  • 自适应优化:经常调用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化

  • 芯片级直接执行:直接编写机器码的

执行引擎工作过程

javac编译过程

  • 词法分析:先读取源代码的字节流数据,然后根据源码语言的语法规则找出源代码中的定义的语言关键字,如if、else、while、for等,然后判断这些关键字的定义是否合法,对于合法的关键字生成用于语法分析的记号序列,同时创建符号表,将所有的标识符记录在符号表【收集属性信息】中这个过程就被称为词法分析

  • 语法分析:对词法分析后得到的Token流进行语法分析,就是依据源程序的语法规则,检查这些关键词组合在一起是否符合Java语言规范,比如if的后面是不是紧跟着一个布尔型判断表达式、else是否写在if后面等。对于符合规范的,组织上一步产生的记号序列生成语法树。形成一颗符合Java语言规定的抽象语法树。抽象语法树是一个结构化的语法表达形式,它的作用是把语言的主要词法用一个结构化的形式组织在一起,这棵语法树可以被后面按照新的规则再重新组织。

  • 语义分析:经过语法分析后就不存在语法错误这些问题了,语义分析主要任务有两个,一个是对上步产生的语法树进行检查,其中包括类型检查、控制流检查、唯一性检查等,第二个则是将一些复杂的语法转换为更简单的语法,相当于把一些文言文、古诗、成语翻译成大白话的意思。比如将foreach转化为for循环、循环标志位替换为break等。

  • 字节码生成:将简化后的语法树转换为Class文件的格式,也就是在该阶段会根据简化后的语法树生成字节码

执行引擎执行过程

  • 编译器代码优化

  • 机械相关优化

  • 寄存器分配优化

  • 目标代码生成器

  • 本地机械指令

JVM执行引擎子系统

解释器

执行一个方法或某处代码,根据定义的规范,对每条需执行的字节码指令逐行解释

JIT及时编译

执行次数比较频繁的代码直接编译成本地机器码

如何判断热点代码

  • 方法调用计数器(Client1500,Server10000;根据方法调用次数

  • 回边计数器(循环体的执行次数

方法调用计数器统计的执行次数并不是绝对次数热度衰减:当超过一定的时间,但计数器还是未达到编译阈值无法提交给JIT即时编译器编译时,那此时就会对计数器进行减半**

  • 采样探测

    周期性的检查每个线程的虚拟机栈栈顶;经常出现在栈顶的方法

    • 优点:实现简单

    • 缺点:无法实现精准探测

  • 踪迹探测

    将一段频繁执行的代码作为一个编译单元;对该单元进行编译

    • 优点:实现复杂

    • 缺点:实现精准探测

为何保留解释器

  • 保证绝对的跨平台性:移除就代表着:每到一个不同的平台,比如从Windows迁移到Linux环境,那么JIT又要重新编译

  • 保证Java启动速度所有的字节码指令全部编译为机器码指令,需要的时间开销是非常巨大

冷热机切换

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值