jvm能够直接运行java字节码,java虚拟机执行的是class字节码指令

  jvm能够直接运行java字节码,java虚拟机执行的是class字节码指令

  JVM中的执行引擎执行java代码时,一般有两种选择:解释执行(由解释器执行)和编译执行(由即时编译器生成的本地代码执行)。

  栈帧

  定义:

  栈是用来支持虚拟机调用和执行方法的数据结构,它位于虚拟机栈中。

  作用:

  每个方法从调用开始到执行完成的过程中,对应的是一个堆栈帧从堆栈到虚拟机堆栈中堆栈的过程。

  如何解决写爬虫IP受阻的问题?立即使用。

  特点:

  (1)堆栈框架包括局部变量表、操作数堆栈等。局部变量表到底有多大,操作数栈有多深,都是在编译时确定的。因为一个堆栈帧需要分配多少内存,不会受到程序运行时可变数据的影响。

  (2)两个堆栈框架之间的数据共享。在概念模型中,两个堆栈框架是完全独立的,但在虚拟机实现中,会做一些优化,使两个堆栈框架部分重叠。这样,在调用方法时,可以共享一部分数据,而不需要额外的参数复制和传递。

  (1)局部变量表

  局部变量表是一组变量值存储空间,用于存储方法参数和方法内部定义的局部变量。

  //方法参数

  max(int a,int b)int a;//全局变量

  void say(){

  int b=0;//局部变量

  }局部变量不同于类变量(用static修饰的变量)

  一个类有两个初始值:准备阶段(系统的初始值)和初始化阶段(程序员的初始值)。所以如果类变量在初始化阶段没有赋值也没关系,它仍然有一个确定的初始值。

  但是,局部变量是不同的。如果定义了它们,但没有给它们初始值,则不能使用它们。

  (2)操作栈

  当一个方法刚开始执行时,它的操作数栈是空的。在方法的执行过程中,会有各种字节码指令从操作数堆栈中写入和提取内容,也就是推送和压入操作。

  例如,计算:

  在int a=2 3操作数堆栈中,最靠近堆栈顶部的两个元素是2和3。iadd指令执行时,2和3会被弹出相加,然后相加的结果5会放入堆栈。

  (3)动态链接

  类文件的常量池中有大量的符号引用,字节码中的方法调用指令以常量池中指向方法的符号引用为参数。这些符号参考分为两部分:

  静态解析:在类加载阶段或第一次使用时,它被转换为直接引用。动态链接:在每次运行期间,它被转换成一个直接引用。

  (4)返回地址

  当一个方法开始执行时,方法只有两种退出方式:正常退出和非正常退出。无论采用什么退出方法,方法退出后,都需要回到调用方法的地方,程序才能继续执行。

  当该方法正常退出时

  呼叫者的PC计数器被用作返回地址。这个计数器值通常保存在堆栈中。

  当该方法异常退出时

  返回地址由异常处理程序表决定。该信息通常不保存在堆栈中。

  方法调用

  方法调用是确定调用哪个方法。

  (1)解析

  调用“编译器知道运行时是不可变的”的方法叫做解析。满足这一要求的方法主要包括

  静态方法,用静态方法修饰的私有方法,用私有方法修饰的私有方法

  (2)分派

  它解释了虚拟机如何确定正确的目标方法。调度分为静态调度和动态调度。在解释静态和动态赋值之前,让我们看一个多态的例子。

  人类人类=新人类();在这段代码中,Human是一个静态类型,这在编译时是已知的。Man是实际类型,结果只能在运行时确定。在编译时,对象的实际类型在编译时是未知的。

  静态分配:

  所有依靠静态类型来定位方法的执行版本的调度操作都称为静态调度。它的典型应用是重载。

  公共类StaticDispatch{

  静态抽象类Human{

  }

  静态类Man扩展人类{

  }

  静态类女人扩展人类{

  }

  公共虚空说(人类的嗡嗡声){

  System.out.println(我是人);

  }

  公共空说(男人哼){

  System.out.println(我是人);

  }

  公共空间说(女人哼){

  System.out.println(我是女人);

  }

  公共静态void main(String[] args){

  人类人类=新人类();

  人类女性=新女性();

  static dispatch Sr=new static dispatch();

  萨伊(男人);

  萨伊(女人);

  }

  }运行结果是:

  我是人类

  为什么我人类会产生这种结果?

  因为当编译器重载时,是通过参数的静态类型而不是实际类型来判断的。在编译阶段,javac编译器会根据参数的静态类型决定使用哪个重载版本,所以对say()方法的两次调用实际上是sr.say(Human)。

  动态分配:

  在运行时,根据实际的类型确定方法来执行版本的分派过程。它的典型应用是重写。

  公共类DynamicDispatch{

  静态抽象类Human{

  受保护的抽象void say();

  }

  静态类Man扩展人类{

  @覆盖

  受保护的抽象void say(){

  System.out.println(我是人);

  }

  }

  静态类女人扩展人类{

  @覆盖

  受保护的抽象void say(){

  System.out.println(我是女人);

  }

  }

  公共静态void main(String[] args){

  人类人类=新人类();

  人类女性=新女性();

  man . say();

  woman . say();

  男人=新女人();

  man . say();

  }

  }运行结果:

  我是人

  我是女人

  我女,这好像是我们平时打字的java代码。对于方法重写,只有在运行时才确定调用哪个方法。因为人的实际类型是人,所以叫人的命名方法。其余的也一样。

  动态分派的实现依赖于方法区中的虚拟方法表,该表存储了每个方法的实际入口地址。如果一个方法在子类中被覆盖,子类方法表中的地址将被指向子类实现版本的入口地址替换;否则,它将指向父类的实现条目。

  单一和多重派遣:

  方法的接收方和方法的参数统称为方法的参数,根据调度基于多少个参数可以分为单调度和多调度。

  在静态调度中,需要调用者的实际类型和方法参数的类型来确定方法版本,所以它是一个多调度类型。在动态调度中,参数的实际类型是已知的,所以只有知道方法调用方的实际类型才能确定方法版本,所以是单一的调度类型。综上所述,java是一种静态多调度,动态单调度的语言。

  字节码解释执行引擎

  虚拟机中的字节码解释执行引擎是基于堆栈的。这里有一段代码来仔细看看它的解释的执行过程。

  public int calc(){

  int a=100

  int b=200

  int c=300

  return(a b)* c;

  }第一步:在堆栈上放100。

  步骤2:操作栈中的100被弹出并存储在一个局部变量中。接下来的200,300都一样。

  第三步:将局部变量表中的100复制到操作数栈顶。

  第四步:将局部变量表中的200复制到操作数栈顶。

  第五步:从栈中取出100和200,做整数加法,最后把结果300放回栈中。

  第六步:将第三个数字300从局部变量表复制到栈顶。下一步是堆叠两个300,进行整数乘法运算,堆叠最后的结果90000。

  第七步:结束方法,将操作数栈顶的整数值返回给这个方法的调用者。

  关于JAVA虚拟机的——字节码执行引擎就说这么多了。更多相关问题请访问PHP中文网:JAVA视频教程。以上是JAVA虚拟机(JVM) (VI)的详细介绍。请多关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: