JVM学习笔记 03 - 字节码

字节码

        一句话概括 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,它的中间接口就是字节码

  • JVM:等同于操作系统;
  • Java 字节码:等同于汇编语言。

        Java 字节码一般都比较容易读懂,这从侧面上证明 Java 语言的抽象程度比较高。你可以把 JVM 认为是一个翻译器,会持续不断的翻译执行 Java 字节码,然后调用真正的操作系统函数,这些操作系统函数是与平台息息相关的。

        使用 JDK 的工具 javac 进行编译后,会产生 HelloWorld 的字节码。
        我们一直在说 Java 字节码是沟通 JVM 与 Java 程序的桥梁,下面使用 javap 来稍微看一下字节码到底长什么样子。

0 getstatic #2 
3 ldc #3 
5 invokevirtual #4 
8 return

        Java 虚拟机采用基于栈的架构,其指令由操作码和操作数组成。这些字节码指令,就叫作 opcode。其中,getstatic、ldc、invokevirtual、return 等,就是 opcode,可以看到是比较容易理解的。
        我们继续使用 hexdump 看一下字节码的二进制内容。与以上字节码对应的二进制,就是下面这几个数字(可以搜索一下)。

b2 00 02 12 03 b6 00 04 b1

注意:这里是二进制文件的16进制表示,也就是hex,一般分析二进制文件都是以hex进行分析。

        我们可以看一下它们的对应关系。

0xb2   getstatic       获取静态字段的值
0x12   ldc             常量池中的常量值入栈
0xb6   invokevirtual   运行时方法绑定调用方法
0xb1   return          void 函数返回

代码例子

public long test(long);
   descriptor: (J)J
   flags: ACC_PUBLIC
   Code:
     stack=4, locals=5, args_size=2
        0: aload_0
        1: getfield      #2                  // Field a:I
        4: i2l
        5: lload_1
        6: ladd
        7: getstatic     #3                  // Field C:J
       10: ladd
       11: lstore_3
       12: lload_3
       13: lreturn
     LineNumberTable:
       line 13: 0
       line 14: 12
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0      14     0  this   LB;
           0      14     1   num   J
          12       2     3   ret   J

执行过程

        回顾一下 JVM 运行时的相关内容。main 线程会拥有两个主要的运行时区域:Java 虚拟机栈和程序计数器。其中,虚拟机栈中的每一项内容叫作栈帧,栈帧中包含四项内容:局部变量报表、操作数栈、动态链接和完成出口。
我们的字节码指令,就是靠操作这些数据结构运行的。下面我们看一下具体的字节码指令。

JVM学习笔记 03 - 字节码_第1张图片

        字节码的执行流程可以这样理解:字节码在Java虚拟机栈中被执行,每一项内容都可以看作是一个栈帧,栈帧的结构包括局部变量表、操作数栈、链接、返回地址。这时候就很明了了,栈帧的执行流程就是字节码的执行流程了。类中变量会被解析到局部变量表,然后对操作数栈进行入栈出栈的操作,在此期间有可能引用到动态或静态链接,最后把计算结果的引用地址返回。

查看字节码

命令行查看字节码

        使用下面的命令编译源代码 A.java。如果你用的是 Idea,可以直接将参数追加在 VM options 里面。

javac -g:lines -g:vars A.java

        这将强制生成 LineNumberTable 和 LocalVariableTable。
        然后使用 javap 命令查看 A 和 B 的字节码。

javap -p -v A
javap -p -v B

        这个命令,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。

可视化查看字节码

        我们可以使用更加直观的工具 jclasslib,来查看字节码中的具体内容了。

JVM学习笔记 03 - 字节码_第2张图片

JVM学习笔记 03 - 字节码_第3张图片

        我们观察到还有 LineNumberTable 等选项,该属性的作用是描述源码行号与字节码行号(字节码偏移量)之间的对应关系。有这些信息,在 debug 时,就能获取到发生异常的源代码行号。 

第04讲:动手实践:从栈帧看字节码是如何在 JVM 中进行流转的

第02讲:大厂面试题:你不得不掌握的 JVM 内存管理

你可能感兴趣的:(学习)