JVM字节码技术

JVM字节码技术

不积细流无以成江河

1.什么是字节码为什么要学习字节码

Java 字节码:

bytecode,是Java代码编译后的中间代码格式。JVM需要读取并解析字节码才能执行相应的任务。JVM字节码是JVM的指令集。JVM加载字节码格式的class文 件,校验之后通过编译器转换为本地机器代码执行。

java bytecode 由单个字节组成(所以java的操作码又叫做字节码),所以最多有256个操作吗。实际上Java只使用了200左右的操作码, 还有一些操作码则保留给调试操作。

分类(指令性质):

  • 程序流程控制指令
  • 算术运算指令以及类型转换指令
  • 对象操作指令
  • 栈操作指令 (包括局部变量交互指令)

学习字节码的好处?

1.了解字节码及其工作原理,对于编写高性能代码至关重要。

2.对于深入分析和排查问题也有一定作用,所以我们要想深入了解JVM来说,了解

字节码也是夯实基础的一项基本功

3.同时对于我们开发人员来时,不了解平台的底层原理和实现细节,想要职业进阶绝对不是长久之计,毕竟我们都希望成为更好的程序

员,。


2.获取字节码齐清单

java代码:

public class HelloByteCode {
    public static void main(String[] args) {
        HelloByteCode helloByteCode = new HelloByteCode();
    }
}
2.1编译反编译(助记符)
javac HelloByteCode.java
javap -c HelloByteCode
警告: 二进制文件HelloByteCode包含com.zhang.demo.HelloByteCode
Compiled from "HelloByteCode.java"
public class com.zhang.demo.HelloByteCode {
	//上边为反编译之后的类, 原类里边只有一个方法 编译反编译后 就出现了两个方法,我们都知道再某些情况下 会自动  生成构造方法,这里验证了构造方法是在编译期间自动生成的 。
  public com.zhang.demo.HelloByteCode();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/zhang/demo/HelloByteCode
       3: dup
       4: invokespecial #3                  // Method "":()V
       7: astore_1
       8: return
}

2.2 查看常量池信息

常量池 大家应该都听说过, 英文是 Constant pool 。这里做一个强调: 大多数时候 指的是 运行时常量池 。但运行时常量池里面的常量是从哪里来的呢? 主要就是由 class 文件中的 常量池结构体 组成的。

javap -c -verbose HelloByteCode

警告: 二进制文件HelloByteCode包含com.zhang.demo.HelloByteCode
Classfile /Users/zy1994/Desktop/zhangyang/AAAA-jk/jvm/code/demo/src/main/java/com/zhang/demo/HelloByteCode.class
  Last modified 2020-10-30; size 303 bytes
  MD5 checksum 2a667aec3dbde61f2a4ffd2412d0acd8
  Compiled from "HelloByteCode.java"
public class com.zhang.demo.HelloByteCode
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#13         // java/lang/Object."":()V
   #2 = Class              #14            // com/zhang/demo/HelloByteCode
   #3 = Methodref          #2.#13         // com/zhang/demo/HelloByteCode."":()V
   #4 = Class              #15            // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               main
  #10 = Utf8               ([Ljava/lang/String;)V
  #11 = Utf8               SourceFile
  #12 = Utf8               HelloByteCode.java
  #13 = NameAndType        #5:#6          // "":()V
  #14 = Utf8               com/zhang/demo/HelloByteCode
  #15 = Utf8               java/lang/Object

SourceFile: "HelloByteCode.java"

major version: 52 (jdk的版本是从45开始的 所以当前的jdk版本是8)

flags: ACC_PUBLIC, ACC_SUPER

ACC_PUBLIC 说明该类是一个PUBLIC的类 ,ACC_SUPER 仅仅是jdk的历史遗留问题需要一直加上的。

我们可以看到常量池中有大量的#号,这就是通常所说的符号引用,因为在类加载之前jvm是不知道这些类方法的具体地址的,所以就需要用一个符号引用先进性代替,再链接的解析阶段这些符号引用才会转化成具体的引用。

那下边的这天语句来进行解析一下常量池:

#1 = Methodref #4.#13 // java/lang/Object."")V

  • Methodref 当前符号引用(常量引用)指向的是一个方法。
  • 记下来可以看到是调用一个类的方法 根据常量表查找可以看到是Object类的init方法 当然后边的注释也进行了解读,方法的返回值 V 就是没有返回值的意思。
2.3 查看方法信息

栈帧(Frame)模型 java的线程栈是由多个栈帧组成的,每一次方法调用都会生成一个栈帧 压入栈中

下图为栈帧的构成:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8P4myR5-1604280603056)(/Users/zy1994/Library/Application Support/typora-user-images/image-20201030165131966.png)]

栈帧主要有三个部分构成:

  • 局部变量数组
    • 存储的是局部变量的值或者引用 包含了方法的参数和局部变量的参数 (编译时确定)
  • 操作数栈
    • FILO的数据结构主要是执行jvm的操作指令
  • class引用

编译和反编译

javac -g xxx
javap -c -verbose xxx

创建对象指令

public com.zhang.demo.HelloByteCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/zhang/demo/HelloByteCode;

你可能感兴趣的:(java,jvm)