解读 Java 字节码

JVM 数据类型

Java是静态类型的,它会影响字节码指令的设计,这样指令就会期望自己对特定类型的值进行操作。例如,就会有好几个add指令用于两个数字相加:iadd、ladd、fadd、dadd。他们期望类型的操作数分别是int、long、float和double。大多数字节码都有这样的特性,它具有不同形式的相同功能,这取决于操作数类型。

JVM定义的数据类型包括

1. 基本类型

- 数值类型:byte(1个字节),short(2个字节),char(2个字节),int(4个字节),float(4个字节),long(8个字节),double(8个字节)
- 布尔类型
- 指针类型:指令类型

2. 引用类型

- 类
- 数组
- 接口

在字节码中布尔类型的支持是受限的。举例来说,没有结构能直接操作布尔值。布尔值被替换转换成 int 是通过编译器来进行的,并且最终还是被转换成 int 结构。

基于栈的架构

字节码指令集的简单性很大程度上是由于 Sun 设计了基于堆栈的 VM 架构,而不是基于寄存器架构。有各种各样的进程使用基于JVM 的内存组件, 但基本上只有 JVM 堆需要详细检查字节码指令:

PC寄存器:对于Java程序中每个正在运行的线程,都有一个PC寄存器保存着当前执行的指令地址。

堆栈:对于每个线程,都会分配一个栈,其中存放本地变量、方法参数和返回值。堆栈指针向下移动,则分配新内存;若向上移动,则释放那些内存。

:所有线程共享的内存和存储对象(类实例和数组)。对象回收是由垃圾收集器管理的。

方法区:对于每个已加载的类,它储存方法的代码和一个符号表(例如对字段或方法的引用)和常量池。

字节码探索

操作码 说明
iconst_1 将整形变量1放入操作数栈顶
bipush 125 将byte类型的125转换成int类型压入栈
istore_1 弹出栈顶元素存入索引为1的局部变量中
iload_1 取出索引为1的局部变量中的数压入栈顶
iadd 从栈顶弹出两个元素然后做加法,将结果再压入栈顶

- 当int取值-1~5时采用iconst指令

int取值0~5时JVM采用iconst_0、iconst_1、iconst_2、iconst_3、iconst_4、iconst_5指令将常量压入栈中,取值-1时采用iconst_m1指令将常量压入栈中。

  • 当int取值-128~127时,JVM采用bipush指令将常量压入栈中。
  • 当int取值-32768~32767时,JVM采用sipush指令将常量压入栈中。
  • 当int取值-2147483648~2147483647时,JVM采用ldc指令将常量压入栈中。

举个栗子

public class Text{
    public static void main(String[] args){
        int a = 1;
        int b = 2;
        int c = a + b;
    }
}

打印被编译的字节码

javac Text.java  //首先进行编译
javap -v Text.class  //运行javap查看字节码

打印出结果很多,我们只看以下部分

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V //1
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: return
         ······
  1. descriptor:说明该方法的参数是一个字符串数组([Ljava/lang/String;),并且返回值是void(V)。
  2. flags:说明该方法是公开的(ACC_PUBLIC),而且是静态的(ACC_STATIC)。

从地址0到8的指令执行说明:

iconst_1:将整型变量1压入栈顶
image

istore_1:弹出栈顶元素存入索引为1的局部变量中
image

iconst_2:将整型变量2压入栈顶
image

istore_2:弹出栈顶元素存入索引为2的局部变量中
image

iload_1:取出索引为1的局部变量中的数压入栈顶
image

iload_2:取出索引为2的局部变量中的压入栈顶
image

iadd:弹出栈顶前两个元素并做加法,然后将结果压入栈顶
image

istore_3:弹出栈顶元素存入索引为3的局部变量中
image

return:从这个void方法中返回

由以上可以看出每一步指令都有JVM精确的执行。

猜你喜欢

转载自blog.csdn.net/swpu_ocean/article/details/80065575