虚拟机字节码执行引擎

java到底是解释语言还是编译语言

  java他是个混合类型的语言,他解释一行行代码执行,同时将反复执行的热点代码,以方法为单位即时编译。因为一个程序里面执行最多的往往是那20%的代码。

不同的即时编译器

  java有多款即时编译器。C1,C2,Graal,这几款是为了在编译时间和运行效率取个均衡。C1是编译时间短,同时性能差,C2正好和C1相反,编译时间稍长,但是性能更高。
  从 Java 7 开始,HotSpot 默认采用分层编译的方式:热点方法首先会被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。

运行时栈帧结构

  java代码加载是放到方法区的,但是在运行过程中,每调用一个方法,就生成一个栈帧。栈帧是用于支持虚拟机进行方法调用的和方法执行的数据结构,是虚拟机运行时数据区的虚拟机栈的栈元素,栈帧存储了方法的局部变量表,操作数栈,动态链接,方法返回地址等。
  每一个栈帧需要多大的内存在编译期间就确认了。

局部变量表

  在其,存放方法参数和方法内部定义的局部变量。

操作数栈

  也叫操作栈,是一个后入先出栈,当一个方法开始执行时,操作数栈是空的,在执行过程中,各种字节码指令会往操作数栈写入和提取内容。

动态连接

  每个栈帧都包含一个指向运行时常量池的引用,在Class文件中会有大量符号引用,一部分会在类加载时就直接转换为直接引用,一部分是在运行时转换为直接引用。

方法返回地址

  方法有2个方式退出,第一个是执行引擎遇到返回的字节码指令,正常退出。第二个就是遇到了异常,且没有被处理也会退出,这个是异常完成退出。无论哪种退出都要返回到方法被调用的位置,程序才能继续执行,正常退出时才会有这个值,异常退出时,一般不会保存这个值。

附加信息

  一些不在规范里面描述的信息,例如调试相关的信息,一般吧动态连接,方法返回地址。

方法调用

  在java中没有传统的连接步骤,存放在Class文件的是符号引用。

解析

  在类加载的解析阶段,会将一部分符号引用变成直接引用。这一部分是有要求的,就是方法调用在编译器就确认好了,不会发生变化的调用。
  在java中符合这个规范(编译器可知,运行期不可变的方法)是静态方法属于这个类,私有方法,外部不可访问,这2个类型的方法注定不会被重写或者继承产生其他版本。因此是在编译器就确认好了。

分派

静态分派

  A a=new B();其中B继承A,其中这个A是静态类型,这个B是实际类型。静态类型编译期间就可以知道确认,而实际类型必须在运行期间在可以确认。编译器不知道一个对象的实际类型是什么,在编译期间。
  在重载中

public void test(A a){...} ...1
public void test(B b){...} ...2

  如果传入A a=new B();把这个对象当参数穿进去,那么调用的是第一个方法,为何?因为这个虚拟机在重载时根据的是参数的静态类型进行判断的,而静态类型却是在编译期间就可以知道的,因此在编译期间就将符号引用确认了。凡是通过静态参数确认的都是静态分配。

动态分配

  动态分配和java中的重写密切相关。动态分配哈java字节码的一些指令相关度很高,他就是更具对象的实际类型类指定的,和对象实际类型相关。这是和java字节码的解析相关的,字节码就是这么解析的。