`

14、jvm字节码执行引擎

 
阅读更多

JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎。

 

虚拟机是相对物理机概念:

物理机的执行引擎 建立在cpu、硬件、指令集、和操作系统 上的指令集

虚拟机的执行引擎 可以自行编制指令集和引擎结构,并且能够执行物理机不支持的指令集。

javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树、再遍历语法树生成线性的字节码指令流的过程。而字节码文件再经过加载、验证、准备、解析、初始化等阶段才能被使用。字节码执行引擎正是执行了这样的过程:输入的是字节码文件,输出的是执行结果。

 

 

运行时栈帧结构:

        栈帧(stack frame)是用于支持虚拟机进行方法调用和方法执行时的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。

        一般把动态连接、方法返回值、和其他信息归档为栈帧信息;

        栈帧包含了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。编译后的class文件,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定,并且写入到方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。

        对于执行引擎来讲,活动线程中,只有栈顶的栈帧是有效的,称为当前栈帧(Curren Stack Frame),这个栈帧所关联的方法称为当前方法(Current Method)。

        执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作
 

1、局部变量表(local variable table):

        用于存放方法参数和方法内部定义的局部变量。

        局部变量表的容量以变量槽(variable slot)为最小单位,jvm规范规定slot应该存放一个boolean,byte,short,int,char,float,reference,returnAddress类型的数据,而没有限定slot的分配内存大小,对于64位的数据类型只有double,long两种(reference可能为32位也可能为64位),这两种类型占用两个slot。

       虚拟机使用局部变量表完成参数传递,如果是实例方法(非static)局部变量表中第0位索引是方法所属对象实例的引用表示this,方法中可以通过this来访问这个隐含的参数。其余参数则按照参数表的顺序来排列,占用从1开始的局部变量slot,参数表分配完毕之后,再根据方法体内部定义的局部变量顺序和作用域分配其余的slot。

       为了尽可能节省栈帧空间,局部变量中的slot可以重用,方法体中定义的变量作用域可能不会整个方法体,如fun(){{int a ;}} ,当字节码pc计数器超过了某个值,这个slot就会交给其他变量使用,但在某些情况下会影响gc收集行为;

main(){
{
byte[] nowa = new bate[10*1024*1024];
}
//int a =0;
System.gc();
}}

  如果不执行int a = 0这条语句,slot就没有回收,这个零值很有意义:如果nowa是个大对象/大对象数组,占耗很大的内存而不及时清空将影响jvm整体的运行速度,手动设零值,释放内存;java一本非常著名的书籍《practical java》说到"把不使用的对象应手动赋值为null";

 

2、操作栈数

         operand stack 也称为操作栈,遵循先入先出(FIFO),同局部变量一样,操作数栈的最大深度在编译时就确定,写入到Code属性的max_stacks数据项中,操作数栈的每一个元素可以使任意的java数据类型,包括long和double型,32位数据类型所占的栈容量为1,64位数据类型所占栈容量为2;

         当一个方法开始执行时,这个方法的操作数栈是空的,在方法执行过程中,会有各种字节码指令向操作数栈中写入和提取内容。比如,加法的字节码指令iadd在运行时会将栈顶两个元素(栈帧)相加并出栈,再将结果入栈。

在编译器和校验阶段的保证下,操作数栈中元素的数据类型必须与字节码指令的序列严格匹配;在大多数vm会将连续的2个栈帧做出部分重叠,重叠部分为部分局部变量表,使用时共用一个变量表,无须额外传递参数;

 

3、动态连接dynamic linking

        每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用(方法区)持有这个引用是为了支持方法调用过程中的动态连接。

        常量池中的方法符号引用,方法符号在类加载阶段(加载、验证、准备、解析、初始化)或者第一次使用时转化为直接引用,这种转化称为静态解析;若在每次运行时都要转换为直接引用的称为动态连接;

 

4、方法返回地址

有两种方式退出当前执行的方法:

一是执行引擎遇到任意一个方法返回的字节码指令,这种方法称为正常完成出口。

二是在方法执行过程中遇到无法处理的异常,这种方法称为异常完成出口。

 

无论哪种方法,方法退出后,都需要返回到调用者的位置,正常退出时,调用者的PC计数器值可以作为返回地址,栈帧会保存这个计数器值,而异常退出时,返回地址要通过异常处理器表来确定。

方法退出的过程实际上是将当前栈帧出战,并恢复上层方法的局部变量表和操作数栈,把返回值传入调用者的操作数栈中,调整pc计数器值、

 

5、 栈帧信息

5.1、 方法调用

方法调用不等于方法执行,方法调用阶段(这是阶段)其唯一的任务就是确定要执行的那个具体方法,暂时不会执行运行;一切方法在字节码里都是引用符号,而不是实际方法的入口(直接引用),这个特性给了java强大的扩张能力,如动态代理,但也使java调用过程变得更复杂,在类加载阶段和运行时才能确定直接引用!

 

5.2、解析(阶段)

所用被调用的方法在字节码文件里都是一个符号引用,在类加载阶段,有的会转化为直接引用,而有的要执行时才转化为直接引用,在加载阶段转化的条件:方法是一个可确定的调用版本,并且这个方法的调用版本是固定的,这个方法会在编译时就确定下了,这类方法的调用称为解析。

JVM提供了4条方法调用的字节码指令:

  1. invokestatic:调用静态方法
  2. invokespecial:调用实例构造器<init>方法,私有方法和父类方法
  3. invokevirtual:调用所有的虚方法
  4. invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
  5. invokedynamic

解析阶段是将(唯一确定的)方法加载到方法区,并不会执行, 能被invokestatic和invokespecial调用的方法,即可在解析阶段加载,他们是唯一确定的,这4类方法称为实方法,其他方法称为虚方法;

编写一个static方法:

public class Demo{
  public static void say(){
     System.out.println("ok");
	 }
public static void main(String... s){
 Demo.say();
}
}
用javap -verbose Demo.class打开
public Demo();
1: invokespecial #1                  // Method java/lang/Object."<init>":()V
public static void say();
0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
public static void main(java.lang.String...);
0: invokestatic  #5                  // Method say:()V

 

5.3、分派

java的3个基本特征:继承、封装、多态;方法的重载和重写。

1,静态分派(static dispatch)---重载

Parent father = new Son();

Parent被称为静态类型(Static Type)或者叫外观类型(Apparent Type),,Son称为实际类型(Actual Type)。

虚拟机(编译器)重载时通过参数的静态类型作为判断依据。所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派;

Demo d = new Demo();
d.say(w);
d.say(m);

两次调用d的say方法,编译器重载say方法(第一次叫加载,第二次叫重载),通过参数的静态类型做出方法重载(方法重载:1.参数类型,2.参数数量),依靠参数来定位方法执行版本的分派动作,都称为静态分派。静态分派的最典型应用就是方法重载。 静态分派发生在编译阶段,静态分派操作不是由虚拟机执行,编译器重载时确定重载版本("最适合的版本")。

void say(Object obj){}
void say(char c){} 

 比如 say('a');会执行第二个方法而不是第一个,因为在方法重载时编译器确定了第二个方法是最适合的版本,而非第一个say()方法;

当出现编译器无法确定该采用何种版本时将拒绝编译。

2,动态分派()---重写

在运行期间,jvm根据实际类型确定方法执行版本的分派过程称为动态分派。

动态分派实现手段“稳定优化”,在类的方法去中建立一个虚方法表(Vritual Method Table,vtable在invokeinterface执行时用用到接口方法表Interface Method Table,itable),使用虚方法表索引来代替元数据查找以提高性能。虚方法表中存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写,那么子类的虚方法表里的入口和父类虚方法入口地址是相同的,都指向父类的入口地址,如果子类重写了父类方法,子类的方法表中保存重写后的方法入口。

3,单分派和多分派

方法(int x = fun(int a,int b);)的接收方x和方法的参数a,b称为方法的宗量,分派基于宗量的种类,分为单分派和多分派两种;

java语言是一门静态多分派(方法重载多分派)、动态单分派的语言(方法重写单分派)”;

在两个say()方法中,存在不同的静态类型:Object和Character,这里宗量是2个,故为多分派类型;

方法表一般在准备阶段初始化,在类变量完成初始零值赋值之后,jvm把实方法,虚方法也进行加载,但不执行。 

 

6、动态语言支持---invokedynamic指令

  • 大小: 9.6 KB
  • 大小: 14.5 KB
  • 大小: 168.4 KB
分享到:
评论

相关推荐

    JVM执行子系统原理

    详细介绍了JVM执行子系统的工作原理,包括类文件结构与字节码指令(Class类文件结构、JVM字节码指令简介)、JVM类加载机制(类加载器、类加载时机、类加载过程)、字节码执行引擎(运行时候的栈结构、方法调用、方法...

    java中jvm原理和实现

    JVM(Java Virtual Machine)是实现Java程序运行的核心部分,它是一个虚拟机,负责将...然后通过执行引擎将字节码指令转换为机器码并执行。同时,JVM还负责垃圾回收和代码优化等任务,以提供高效的Java程序运行环境。

    Java虚拟机(JVM)面试题(总结最全面的面试题!!!)

    Java虚拟机(JVM)面试题(总结最全面的面试题!...能不能解释一下方法区(重点理解)什么是JVM字节码执行引擎你听过直接内存吗?知道垃圾收集系统吗?堆栈的区别是什么?深拷贝和浅拷贝Java会存在内存泄漏吗?请说 收

    超硬核!!!一篇文章搞定整个JVM运行时数据区

    JVM运行时数据区1 JVM运行时数据区2 解析JVM运行时数据区2.1 方法区(Method Area)2.2 Java堆(Java Heap)2.3 程序计数器(Program Counter ...3.1 JVM字节码执行引擎3.2 垃圾收集系统3.3 直接内存(Direct Memory)...

    JVM调优基本概念以及调优的工作流程

    类装载器、运行时数据区(内存模型)、字节码执行引擎 工作大致流程 首先我们的java类编译成class类文件,当我们的class文件开始执行,我们的虚拟机便开始工作。 类加载器将class加载到运行时数据区,然后字节码...

    JVM执行子系统.pdf

    Class 类文件结构、字节码指令、类加载机制以及基于栈的字节码解释执行引擎

    JVM与GC调优课程视频

    ├── 第1篇-字节码篇.png?x-oss-process=style/pnp8 ├── 第2篇-类的加载篇.png?x-oss-process=style/pnp8 ├── 第3篇-运行时内存篇.png?x-oss-process=style/pnp8 ├── 第4篇-对象内存布局.png?x-oss-...

    jvm相关技术分享

    概述 Java内存区域 GC与内存分配策略 虚拟机性能监控与故障处理工具 类文件结构 类加载机制 字节码执行引擎

    JAVA虚拟机精讲 pdf

    HotSpot VM 是目前市面上高性能JVM 的代表作之一,它采用解释器+JIT 编译器的混合执行引擎,使得Java 程序的执行性能从此有了质的飞跃。本书以极其精练的语句诠释了HotSpot VM 的方方面面,比如:字节码的编译原理、...

    JVM——Java虚拟机架构

    JVM=类加载器classloader+执行引擎executionengine+运行时数据区域runtimedataarea首先Java源代码文件被Java编译器编译为字节码文件,然后JVM中的类加载器加载完毕之后,交由JVM执行引擎执行。在整个

    大厂架构师-日均百万订单量的JVM优化与高级GC调优策略实战(5.8G)

    ├─第1篇-字节码篇.png?x-oss-process=style/pnp8 ├─第2篇-类的加载篇.png?x-oss-process=style/pnp8 ├─第3篇-运行时内存篇.png?x-oss-process=style/pnp8 ├─第4篇-对象内存布局.png?x-oss-process=style/pnp...

    深入理解JVM内存结构及运行原理全套视频加资料.txt

     第104讲 字节码执行引擎小结 00:03:38  第105讲 总结与回顾 00:10:55  第106讲 happens-before简单概述 00:15:17  第107讲 重排序问题 00:23:19  第108讲 锁的内存语义 00:13:54  第109讲 volatile的...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 189 7.4.1 类与类加载器 / 189 7.4.2 双亲委派模型 / 191 7.4.3 破坏双亲委派模型 / 194 7.5 本章小结 / 197 第8章 虚拟机字节码执行引擎 / 198 8.1 概述 / 198 8.2 运行时栈帧结构 / 199 8.2.1 局部变量...

    java虚拟机精讲(电子工业出版社出版)

    HotSpot VM是目前市面上高性能JVM的代表作之一,它采用解释器+JIT 编译器的混合执行引擎,使得Java 程序的执行性能从此有了质的飞跃。本书以极其精练的语句诠释了 HotSpot VM的方方面面,比如:字节码的编译原理、...

    jvm虚拟机总结

    Java源码首先被编译成字节码,再由不同平台的JVM进行解析,JAVA语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。 其中导致这个特性最主要的原因...

    深入理解Java虚拟机视频教程(jvm性能调优+内存模型+虚拟机原理)视频教程

    视频目录 第1节说在前面的话 [免费观看] 00:05:07分钟 | 第2节整个部分要讲的内容说明 [免费观看] 00:06:58分钟 | 第3节环境搭建以及jdk,...第104节字节码执行引擎小结00:03:38分钟 | 第105节总结与回顾00:10:55分钟

Global site tag (gtag.js) - Google Analytics