JVM-执行引擎

一、执行引擎概述

JVM核心支撑之一。JVM主要任务就是把字节码加载到内存中在让执行引擎进行执行。执行引擎的任务就是把字节码文件编译成操作系统可识别的的本地机器指令。

执行引擎的工作过程:

  1. 执行引擎在执行过程中究竟需要执行什么样子的字节码指令完全依赖于PC寄存器
  2. 每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址
  3. 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息(方法区)

二、java代码编译和执行过程

JVM-执行引擎_第1张图片

大部分程序代码转换成物理机的目标代码或虚拟机能执行的指令集之前,都需要经过上图中的各个步骤。

橙色的过程就是前端编译,也就是java代码编译成.class文件的过程。

java是半解释、半编译型语言,其中绿色的部分就是解释、浅蓝色的部分就是编译。

解释器:当JVM启动的时候会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。

JIT编译器:就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。缓存到方法区中。

三、机器码、指令、汇编语言

JVM-执行引擎_第2张图片

 机器码

  • 二进制编码方式标识指令
  • 人类难以理解
  • 已经输入计算机,就可以执行,效率最高
  • 不同的cpu机器指令差距很大

指令

  • 因为机器码太难以理解,就法令了指令
  • 不同的硬件平台,指令也不同

指令集

  • 针对不同平台的指令集合,叫做指令集,有X86、ARM等

汇编语言

  • 相比指令,要更高一层的的语言
  • 汇编语言需要执行需要翻译成指令

高级语言

  • java、C、C++等,也需要翻译成机器指令,有编译型、解释型

JVM-执行引擎_第3张图片

四、解释器

字节码存在的原因,主要应该是把代码->机器码分割成了2部分,代码->字节码->机器码,这样效率跟高。

  • 解释器是真正意义上所承担的劫色就是一个运行时“翻译者”,将字节码文件中的内容翻译成对应平台的本地机器指令执行
  • 当一条字节码指令被翻译执行完成后,接着在根据PC寄存器中记录的下一条需要被执行字节码指令进执行解释操作
  • 分为字节码解释器(效率低)、模板解释器(效率高),不论哪种都是低效的代名词,为了解决这个问题,JIT编译器诞生了

五、JIT编译器

JIT(just in time)编译器,会把字节码按照方法为单元,进行编译,并缓存到方法区,优点就是速度快。

  • HotSpot VM采用编译器和解释器并存的架构,在JVM运行时,解释器和编译器能项目协作,各自取长补短,选择最合适的方式来编译本地代码和直接解释执行代码的时间
  • 点阶段,java程序运行性能已经能够C/C++进行比较的地步

既然JIT编译器效率高,为什么要留着解释器呢?

  • 在项目启动的时候,解释器可以立马发挥作用,省去编译的时间,立即执行
  • 当JVM启动后,解释器可以首先发挥作用,不必等着全部编译完才能执行。随着运行时间的推移,根据热点探测技术,将有价值的字节码翻译为本地指令,缓存到方法区
  • JIT编译器出现问题的时候,解释器作为备用方案

在生产发布的时候,一个JVM在热机状态下的承载能力要大于冷机状态,如果以热机状态进行流量切换,那么可能导致处理冷机状态的机器进入无法承受流量而假死。

热点代码和探测方式

是否需要启动JIT编译器将字节码字节编译成本地平台的机器指令,需要根据代码被调用的执行的频率而定。关于那些需要被编译为本地代码的字节码,也称为热点代码,JIT编译器在运行时会针对那些调用频繁的“热点代码”做出深度优化,将其直接翻译为本地平台的本地机器指令,缓存到方法区。这个过程叫做栈上替换,或者(OSR)。

Hotspot基于计数器的热点探测:

  • 调用技术区:方法被调用次数-XX:CompileThreshold,也不是完全绝对的值,每隔一定周期都会进行阈值的衰减,这个时间段也叫做半衰周期,关闭衰减-XX:-UseConterDecay,设置半衰周期的参数是:-XX:ConterHalfLifeTimeJVM-执行引擎_第4张图片
  • 回边计数器:循环体的循环次数

开发人员可以根据具体场景,进行设置:

  • -Xint:完全采用解释器
  • -Xcomp:完全采用JIT编译器,如果编译器出问题,解释器会接入执行
  • -Xminxed:采用2中方式同时存在

Hotspot内置2中编译器,c1=clinet 、c2=server,c2编译器优化更多,但是编译时间更长,默认下C1和C2编译器都使用。

C1优化策略:

  • 方法内联:将引用的函数代码编译到引用点,这样可以减少栈帧的生成,减少参数传递以及跳转过程
  • 去虚拟化:对唯一的实现类进行内联
  • 冗余消除:在运行期间,把一些不会执行的代码折叠掉

C2优化策略:

  • 标量替换:用标量值替代集合对象的属性
  • 栈上分配:对于未逃逸的对象分配在栈上,而不是堆上
  • 同步消除:清除同步操作,通常只synchronized

一般来讲,JIT编译出来的机器码性能比解释器高。C2比C1启动慢,系统稳定执行后,C2的执行速度快于C1。

JIT编译器,是在运行的时候编译热点代码,AOT编译器是在运行之前就编译了。

你可能感兴趣的:(jvm,jvm,java,开发语言)