Java ByteCode

作者: heiheiwanne | 来源:发表于2019-01-08 18:32 被阅读23次

    什么是Java字节码指令?简而言之,Java字节码指令就是Java虚拟机能够听得懂、可执行的指令,可以说是Jvm层面的汇编语言,或者说是Java代码的最小执行单元。

    • 先来看一下Java Class的文件结构

    源文件

    package com.**.lib.lib;
    
    public class MyClass  {
        private String text = "hello world";
    
        public static void main(String[] args) {
            System.out.print("hello world");
        }
    }
    
    class文件

    cd 到class文件下,将class用文本文件打开

    先看文本文件中开头的前4个字节cafe babe ,这4个字节是该文件的魔数,是一个固定值,表示此文件可以被java虚拟机接受,否则虚拟机拒绝执行此文件。

    cafe babe 0000 0033 0022 0a00 0600 1409
    0015 0016 0800 170a 0018 0019 0700 1a07
    001b 0100 063c 696e 6974 3e01 0003 2829
    5601 0004 436f 6465 0100 0f4c 696e 654e
    756d 6265 7254 6162 6c65 0100 124c 6f63
    616c 5661 7269 6162 6c65 5461 626c 6501
    0004 7468 6973 0100 1b4c 636f 6d2f 6c75
    636b 792f 6c69 622f 6c69 622f 4d79 436c
    6173 733b 0100 046d 6169 6e01 0016 285b
    4c6a 6176 612f 6c61 6e67 2f53 7472 696e
    673b 2956 0100 0461 7267 7301 0013 5b4c
    6a61 7661 2f6c 616e 672f 5374 7269 6e67
    3b01 000a 536f 7572 6365 4669 6c65 0100
    0c4d 7943 6c61 7373 2e6a 6176 610c 0007
    0008 0700 1c0c 001d 001e 0100 0b68 656c
    6c6f 2077 6f72 6c64 0700 1f0c 0020 0021
    0100 1963 6f6d 2f6c 7563 6b79 2f6c 6962
    2f6c 6962 2f4d 7943 6c61 7373 0100 106a
    6176 612f 6c61 6e67 2f4f 626a 6563 7401
    0010 6a61 7661 2f6c 616e 672f 5379 7374
    656d 0100 036f 7574 0100 154c 6a61 7661
    2f69 6f2f 5072 696e 7453 7472 6561 6d3b
    0100 136a 6176 612f 696f 2f50 7269 6e74
    5374 7265 616d 0100 0570 7269 6e74 0100
    1528 4c6a 6176 612f 6c61 6e67 2f53 7472
    696e 673b 2956 0021 0005 0006 0000 0000
    0002 0001 0007 0008 0001 0009 0000 002f
    0001 0001 0000 0005 2ab7 0001 b100 0000
    0200 0a00 0000 0600 0100 0000 0300 0b00
    0000 0c00 0100 0000 0500 0c00 0d00 0000
    0900 0e00 0f00 0100 0900 0000 3700 0200
    0100 0000 09b2 0002 1203 b600 04b1 0000
    0002 000a 0000 000a 0002 0000 0005 0008
    0006 000b 0000 000c 0001 0000 0009 0010
    0011 0000 0001 0012 0000 0002 0013 
    

    cd 到class文件下 ,执行命令(这里使用javac命令也可以)

    javap -v -p MyClass
    

    生成字节码文件:

    Classfile /Users/.../MyClass.class
      Last modified 2019-1-8; size 614 bytes
      MD5 checksum 24c88198062311ff001f795f779bd2b1
      Compiled from "MyClass.java"
    public class com.lucky.lib.lib.MyClass
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #7.#23         // java/lang/Object."<init>":()V
       #2 = String             #24            // hello world
       #3 = Fieldref           #6.#25         // com/lucky/lib/lib/MyClass.text:Ljava/lang/String;
       #4 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
       #5 = Methodref          #28.#29        // java/io/PrintStream.print:(Ljava/lang/String;)V
       #6 = Class              #30            // com/lucky/lib/lib/MyClass
       #7 = Class              #31            // java/lang/Object
       #8 = Utf8               text
       #9 = Utf8               Ljava/lang/String;
      #10 = Utf8               <init>
      #11 = Utf8               ()V
      #12 = Utf8               Code
      #13 = Utf8               LineNumberTable
      #14 = Utf8               LocalVariableTable
      #15 = Utf8               this
      #16 = Utf8               Lcom/lucky/lib/lib/MyClass;
      #17 = Utf8               main
      #18 = Utf8               ([Ljava/lang/String;)V
      #19 = Utf8               args
      #20 = Utf8               [Ljava/lang/String;
      #21 = Utf8               SourceFile
      #22 = Utf8               MyClass.java
      #23 = NameAndType        #10:#11        // "<init>":()V
      #24 = Utf8               hello world
      #25 = NameAndType        #8:#9          // text:Ljava/lang/String;
      #26 = Class              #32            // java/lang/System
      #27 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
      #28 = Class              #35            // java/io/PrintStream
      #29 = NameAndType        #36:#37        // print:(Ljava/lang/String;)V
      #30 = Utf8               com/lucky/lib/lib/MyClass
      #31 = Utf8               java/lang/Object
      #32 = Utf8               java/lang/System
      #33 = Utf8               out
      #34 = Utf8               Ljava/io/PrintStream;
      #35 = Utf8               java/io/PrintStream
      #36 = Utf8               print
      #37 = Utf8               (Ljava/lang/String;)V
    {
      private java.lang.String text;
        descriptor: Ljava/lang/String;
        flags: ACC_PRIVATE
    
      public com.lucky.lib.lib.MyClass();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String hello world
             7: putfield      #3                  // Field text:Ljava/lang/String;
            10: return
          LineNumberTable:
            line 3: 0
            line 4: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      11     0  this   Lcom/lucky/lib/lib/MyClass;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #2                  // String hello world
             5: invokevirtual #5                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 7: 0
            line 8: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
    }
    SourceFile: "MyClass.java"
    

    下面来专门讲解下字节码:
    前几行表示的是类信息(这些不属于字节码文件内容)

    Last modified 2019-1-8; /*修改时间*/ size 558 bytes /*文件大小*/ 
    MD5 checksum fc4430807406c90f9088a7bb465b432d /*文件的md5*/ 
    Compiled from "MyClass.java" /* 文件的源码文件 */ 
    

    往下看

    • 1.魔数与Class文件版本
      上边说过魔数是cafe babe
      主次版本号
    minor version: 0  //编译次版本号
    major version: 51 //编译主版本号
    

    参考下图得知我们的编译版本号是java 1.7


    主次版本号

    还有一些其他的标记以及解释


    • 2.常量池 constant_pool
      每个常量池的常量都用一个类型为 cp_info 的表表示,该表有 14 个值,分别是:


     #1 = Methodref          #7.#23         // java/lang/Object."<init>":()V
    

    此常量为方法引用类型(CONSTANT_MethodHandle_info)的常量,
    值指向了#7.#23的信息,也就是后边描述符 java/lang/Object."<init>":()V

     #2 = String             #24            // hello world
    

    表示该常量为字符串引用类型(CONSTANT_String_info)的常量。指向了#24的信息,也就是hello world

    #3 = Fieldref           #6.#25         // com/lucky/lib/lib/MyClass.text:Ljava/lang/String;
    

    此常量是一个字段引用类型CONSTANT_Fieldref_info)的常量。此类信息指向了 #6.#25的值,也就是com/lucky/lib/lib/MyClass.text:Ljava/lang/String;

     #24 = Utf8               hello world
    

    此常量是一个字符串常量,转换之后是:hello world
    可以看出常量池是相互复用的。

    • 3.访问标志
      在常量池结束之后,紧接着的两个字节代表访问标记(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口、是否定义为public类型、是否定义为abstract类型等。
    flags: ACC_PUBLIC, ACC_SUPER
    

    具体的标志位以及标志的含义见下表。


    • 4.类索引、父类索引、接口索引(略)
    • 5.字段表集合
      字段表用于描述接口或者类中生命的变量,这里说的字段包括类级变量和实例级变量,但不包括在方法内部声明的局部变量。
      字段表的每个字段用一个名为 field_info 的表来表示,field_info 表的数据结构如下所示:


    private java.lang.String text;
        descriptor: Ljava/lang/String;  //字段描述符 为String
        flags: ACC_PRIVATE //字段访问标志 private
    
    • 6.方法表集合:描述了每个方法
      方法表中的每个方法都用一个 method_info 表示,其数据结构如下:


    public com.lucky.lib.lib.MyClass(); 
        descriptor: ()V  //方法描述 此方法是MyClass的构造方法
        flags: ACC_PUBLIC //public
        Code:  //代码部分
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String hello world
             7: putfield      #3                  // Field text:Ljava/lang/String;
            10: return
          LineNumberTable:
            line 3: 0
            line 4: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               0      11     0  this   Lcom/lucky/lib/lib/MyClass;
    

    下面详细解释下代码部分
    stack=2, locals=1, args_size=1
    stack:操作数栈的深度。locals:局部变量表大小 args_size:方法参数个数
    0:aload_0: 将第一个参数压入栈中,即this(对应LocalVariableTable中Slot的0局部变量)。
    1: invokespecial #1: 使用invokespecial指令调用一个特殊的初始化方法 java/lang/Object."<init>":()V 其中()代表的是这个方法的参数,后面跟的是这个方法的返回值类型,V代表void,即无返回值。
    4: aload_0: 将第一个参数压入栈
    5: ldc #2: 使用ldc将常量池中的字符串指针(即"Hello World")压入栈中,ldc指令表示将一个常量池中的对象压入操作数栈中.
    7: putfield #3: putfiel指令消耗了栈顶的两个操作数(“Hello World",this),并将栈顶的元素放入到栈里第二个元素的对应的field内部,putfield只弹出栈内的操作数,而没有向操作数栈压回任何数据,而且执行putfield之前,栈内元素的位置也必须符合“值在上,主体在下”要求。
    10: return: return仅表示方法结束,而不会像areturn一样返回栈顶元素。

    下表列出了一些返回值符号对应的含义


    public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #2                  // String hello world
             5: invokevirtual #5                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 7: 0
            line 8: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
    
    

    0: getstatic #4:getstatic指令获取java/lang/System.out:Ljava/io/PrintStream的静态域,再将它压入栈中(java/io/PrintStream的实例)
    3: ldc #2 :ldc将常量池中的字符串指针(即"Hello World")压入栈中,ldc指令表示将一个常量池中的对象压入操作数栈中.
    5: invokevirtual #5:invokevirtual调用java/io/PrintStream.println,invokevirtual指令用于调用实例的方法
    8: return:return指令返回

    参考:
    从 HelloWorld 看 Java 字节码文件结构
    大话+图说:Java字节码指令——只为让你懂
    Java字节码指令
    字节码一览表(墙)

    相关文章

      网友评论

        本文标题:Java ByteCode

        本文链接:https://www.haomeiwen.com/subject/uwlvrqtx.html