跳到主要内容

01 Java虚拟机基本原理

01 | Java代码是怎么运行的?

Java 虚拟机具体是怎样运行 Java 字节码的?

  • 虚拟机视角: 把由 Java 代码编译成的 class 文件加载到 Java虚拟机中,加载后的 Java类 会被存放于方法区(Method Area)中,实际运行时Java虚拟机会执行方法区中的代码
    • Java虚拟机 在内存中划分堆和栈来存储运行时数据
      Java运行时内存区域
    • Java虚拟机 将栈细分为 面向Java方法的Java方法栈、面向本地方法(C++编写的native方法)的本地方法栈、以及存放各个线程执行位置的 PC寄存器
    • 每当调用进入一个Java方法,Java虚拟机 会在当前线程的Java方法栈中生成一个栈帧,用于存放局部变量以及字节码的操作数。当退出当前执行的方法时,无论正常返回或者异常返回,Java虚拟机都会弹出当前线程的当前栈帧并抛弃
    • 栈帧的大小是提前计算好的,并且 Java虚拟机 并不要求栈帧在内存空间里连续分布
  • 硬件视角: Java字节码无法直接执行,Java虚拟机 需要将字节码翻译成机器码
    翻译过程
    • 翻译过程的两种形式:
      1. 解释执行: 逐条将字节码编译成机器码并执行
      2. 即时编译(Just-In-Time,JIT): 将一个方法中包含的所有字节码编译成机器吗后再执行
    • 解释执行的优势在于无需等待编译,即时编译的优势在于实际运行速度更快。HotSpot默认采用混合编译,先解释执行字节码,然后将其中反复执行的热点代码以方法为单位进行即时编译

Java 虚拟机的运行效率究竟是怎么样的?

  • 即时编译建立在程序符合二八定律的假设上,即百分之二十的代码占据了百分之八十的计算资源
  • 引入多个编译器: 在编译时间和生成代码的执行效率之间进行取舍,满足不同用户场景的需求
  • 编译器:
    • C1: Client编译器,面向对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,编译时间较短
    • C2: Server编译器,面向对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,编译时间较长,但生成的代码执行效率较高
  • 分层编译: Java7 开始 HotSpot 默认采用的编译方式,热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译
  • HotSpot 的即时编译放在额外的编译线程中进行,以免干扰应用的正常运行。HotSpot 会根据CPU的数量设置编译线程的数目,按照1:2的比例配置给C1和C2编译器
  • 计算资源充足的情况下,字节码的解释执行和即时编译可同时进行,编译完成后的机器码会在下次调用该方法时启用,替换原本的解释执行

02 | Java的基本类型

  • 使用基本类型能够在执行效率以及内存使用两方面提升软件性能

Java虚拟机的Boolean类型

  • 在Java虚拟机规范中,boolean类型被映射成int类型,true被映射成整数1,false被映射成整数0

Java的基本类型

  • Java的基本类型:
    基本类型
    • byte、short、int、long、float、double 的值域依次扩大,前面的值域被后面的值域所包含,因此前面的基本类型转换至后面的基本类型,无需强制转换,默认值看起来不相同,但在内存中均为0
    • boolean、char 是唯二的无符号类型,在不考虑违反规范的情况下,boolean的取值范围是0或1,char类型的取值范围是[0,65535],通常可以认为char类型的值为非负数(用途: 可以作为数组索引等)

Java基本类型的大小

  • 解释器使用的解释栈帧(interpreted frame): 有主要的两个组成部分,分别是局部变量区字节码的操作数栈
  • 在Java虚拟机规范中,局部变量区等价于一个数组,并且可以使用正整数来索引,除了long、double类型需要使用两个数组单元来存储之外,其他基本类型以及引用类型的值均占用一个数组单元,即boolean、byte、char、short四种类型在栈上占用的空间和int是一样的,和引用类型也是一样的(32位HotSpot虚拟机: 占用4个字节,64位HotSpot虚拟机: 占用8个字节),这种情况仅存在于局部变量区,而不会出现在存储于堆中的字段或者数组元素上
  • 在堆上,byte、char、short三种类型的字段或者数组单元在堆上占用的空间分别为1字节2字节2字节,与这些类型的值域相吻合
  • 在HotSpot中,boolean字段占用1字节,boolean数组直接使用byte数组来实现