【JVM学习笔记】字节码文件结构实例

上一篇笔记的内容大部分没有实际动手操作,因此决定完成这个完整的练习并记录下来。

另注,idea环境下有jclasslib插件用于更好的查看类似于javap结果的内容。

源代码如下:

package com.learn.jvm;

/**
 * @Description
 * @date 2019/09/05 16:31
 */
public class Test {
    String str = "Welcome";
    private int x = 5;
    public static Integer num = 10;

    public static void main(String[] args) {
        Test test = new Test();
        test.setX(8);
        num = 20;
    }

    private synchronized void setX(int x) {
        this.x = x;
    }

    private void test1(String str) {
        // 注意,这里会对str对象本身加锁,如果str是"abc",则是对"abc"这个字符串对象加锁,如果str是"def",则是对"def"这个对象加锁
        // 所以实际上这种写法无法实现正常的加锁功能,只是了学习字节码而举的例子
        synchronized (str) {
            System.out.println("hello world");
        }
    }

    private synchronized  static void test2(){}
}

编译后在Windows平台下进入wsl,使用命令vim Test.class,然后输入:%!xxd就可以看到16进制代码。

也可以使用Linux下的xxd命令,将二进制信息转换为16进制数据,使用方式为 xxd Test.class Test.txt,生成的HelloWorld.txt与通过:%!xxd是一样的(实际操作时返现在结尾处略有不同,生成txt文件少了1个字节)

00000000: cafe babe 0000 0033 0046 0a00 0d00 2d08  .......3.F....-.
00000010: 002e 0900 0500 2f09 0005 0030 0700 310a  ....../....0..1.
00000020: 0005 002d 0a00 0500 320a 0033 0034 0900  ...-....2..3.4..
00000030: 0500 3509 0036 0037 0800 380a 0039 003a  ..5..6.7..8..9.:
00000040: 0700 3b01 0003 7374 7201 0012 4c6a 6176  ..;...str...Ljav
00000050: 612f 6c61 6e67 2f53 7472 696e 673b 0100  a/lang/String;..
00000060: 0178 0100 0149 0100 036e 756d 0100 134c  .x...I...num...L
00000070: 6a61 7661 2f6c 616e 672f 496e 7465 6765  java/lang/Intege
00000080: 723b 0100 063c 696e 6974 3e01 0003 2829  r;...<init>...()
00000090: 5601 0004 436f 6465 0100 0f4c 696e 654e  V...Code...LineN
000000a0: 756d 6265 7254 6162 6c65 0100 124c 6f63  umberTable...Loc
000000b0: 616c 5661 7269 6162 6c65 5461 626c 6501  alVariableTable.
000000c0: 0004 7468 6973 0100 144c 636f 6d2f 6c65  ..this...Lcom/le
000000d0: 6172 6e2f 6a76 6d2f 5465 7374 3b01 0004  arn/jvm/Test;...
000000e0: 6d61 696e 0100 1628 5b4c 6a61 7661 2f6c  main...([Ljava/l
000000f0: 616e 672f 5374 7269 6e67 3b29 5601 0004  ang/String;)V...
00000100: 6172 6773 0100 135b 4c6a 6176 612f 6c61  args...[Ljava/la
00000110: 6e67 2f53 7472 696e 673b 0100 0474 6573  ng/String;...tes
00000120: 7401 0004 7365 7458 0100 0428 4929 5601  t...setX...(I)V.
00000130: 0005 7465 7374 3101 0015 284c 6a61 7661  ..test1...(Ljava
00000140: 2f6c 616e 672f 5374 7269 6e67 3b29 5601  /lang/String;)V.
00000150: 000d 5374 6163 6b4d 6170 5461 626c 6507  ..StackMapTable.
00000160: 0031 0700 3c07 003b 0700 3d01 0005 7465  .1..<..;..=...te
00000170: 7374 3201 0008 3c63 6c69 6e69 743e 0100  st2...<clinit>..
00000180: 0a53 6f75 7263 6546 696c 6501 0009 5465  .SourceFile...Te
00000190: 7374 2e6a 6176 610c 0014 0015 0100 0757  st.java........W
000001a0: 656c 636f 6d65 0c00 0e00 0f0c 0010 0011  elcome..........
000001b0: 0100 1263 6f6d 2f6c 6561 726e 2f6a 766d  ...com/learn/jvm
000001c0: 2f54 6573 740c 0020 0021 0700 3e0c 003f  /Test.. .!..>..?
000001d0: 0040 0c00 1200 1307 0041 0c00 4200 4301  .@.......A..B.C.
000001e0: 000b 6865 6c6c 6f20 776f 726c 6407 0044  ..hello world..D
000001f0: 0c00 4500 2301 0010 6a61 7661 2f6c 616e  ..E.#...java/lan
00000200: 672f 4f62 6a65 6374 0100 106a 6176 612f  g/Object...java/
00000210: 6c61 6e67 2f53 7472 696e 6701 0013 6a61  lang/String...ja
00000220: 7661 2f6c 616e 672f 5468 726f 7761 626c  va/lang/Throwabl
00000230: 6501 0011 6a61 7661 2f6c 616e 672f 496e  e...java/lang/In
00000240: 7465 6765 7201 0007 7661 6c75 654f 6601  teger...valueOf.
00000250: 0016 2849 294c 6a61 7661 2f6c 616e 672f  ..(I)Ljava/lang/
00000260: 496e 7465 6765 723b 0100 106a 6176 612f  Integer;...java/
00000270: 6c61 6e67 2f53 7973 7465 6d01 0003 6f75  lang/System...ou
00000280: 7401 0015 4c6a 6176 612f 696f 2f50 7269  t...Ljava/io/Pri
00000290: 6e74 5374 7265 616d 3b01 0013 6a61 7661  ntStream;...java
000002a0: 2f69 6f2f 5072 696e 7453 7472 6561 6d01  /io/PrintStream.
000002b0: 0007 7072 696e 746c 6e00 2100 0500 0d00  ..println.!.....
000002c0: 0000 0300 0000 0e00 0f00 0000 0200 1000  ................
000002d0: 1100 0000 0900 1200 1300 0000 0600 0100  ................
000002e0: 1400 1500 0100 1600 0000 4200 0200 0100  ..........B.....
000002f0: 0000 102a b700 012a 1202 b500 032a 08b5  ...*...*.....*..
00000300: 0004 b100 0000 0200 1700 0000 0e00 0300  ................
00000310: 0000 0800 0400 0900 0a00 0a00 1800 0000  ................
00000320: 0c00 0100 0000 1000 1900 1a00 0000 0900  ................
00000330: 1b00 1c00 0100 1600 0000 5700 0200 0200  ..........W.....
00000340: 0000 17bb 0005 59b7 0006 4c2b 1008 b700  ......Y...L+....
00000350: 0710 14b8 0008 b300 09b1 0000 0002 0017  ................
00000360: 0000 0012 0004 0000 000e 0008 000f 000e  ................
00000370: 0010 0016 0011 0018 0000 0016 0002 0000  ................
00000380: 0017 001d 001e 0000 0008 000f 001f 001a  ................
00000390: 0001 0022 0020 0021 0001 0016 0000 003e  ...". .!.......>
000003a0: 0002 0002 0000 0006 2a1b b500 04b1 0000  ........*.......
000003b0: 0002 0017 0000 000a 0002 0000 0014 0005  ................
000003c0: 0015 0018 0000 0016 0002 0000 0006 0019  ................
000003d0: 001a 0000 0000 0006 0010 0011 0001 0002  ................
000003e0: 0022 0023 0001 0016 0000 0085 0002 0004  .".#............
000003f0: 0000 0017 2b59 4dc2 b200 0a12 0bb6 000c  ....+YM.........
00000400: 2cc3 a700 084e 2cc3 2dbf b100 0200 0400  ,....N,.-.......
00000410: 0e00 1100 0000 1100 1400 1100 0000 0300  ................
00000420: 1700 0000 1200 0400 0000 1a00 0400 1b00  ................
00000430: 0c00 1c00 1600 1d00 1800 0000 1600 0200  ................
00000440: 0000 1700 1900 1a00 0000 0000 1700 0e00  ................
00000450: 0f00 0100 2400 0000 1800 02ff 0011 0003  ....$...........
00000460: 0700 2507 0026 0700 2700 0107 0028 fa00  ..%..&..'....(..
00000470: 0400 2a00 2900 1500 0100 1600 0000 1900  ..*.)...........
00000480: 0000 0000 0000 01b1 0000 0001 0017 0000  ................
00000490: 0006 0001 0000 001f 0008 002a 0015 0001  ...........*....
000004a0: 0016 0000 0021 0001 0000 0000 0009 100a  .....!..........
000004b0: b800 08b3 0009 b100 0000 0100 1700 0000  ................
000004c0: 0600 0100 0000 0b00 0100 2b00 0000 0200  ..........+.....
000004d0: 2c0a                                     ,.

javap -verbose -p 的输出结果如下(不待参数p则不会显示私有成员)

D:workspace-learncommon-learnlearn-classloader	argetclassescomlearnjvm>javap -verbose -p Test
警告: 二进制文件Test包含com.learn.jvm.Test
Classfile /D:/workspace-learn/common-learn/learn-classloader/target/classes/com/learn/jvm/Test.class
  Last modified 2019-9-5; size 1233 bytes
  MD5 checksum d7a9950df521ddf0ee5c7b76c1a25796
  Compiled from "Test.java"
public class com.learn.jvm.Test
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#45        //  java/lang/Object."<init>":()V
   #2 = String             #46            //  Welcome
   #3 = Fieldref           #5.#47         //  com/learn/jvm/Test.str:Ljava/lang/String;
   #4 = Fieldref           #5.#48         //  com/learn/jvm/Test.x:I
   #5 = Class              #49            //  com/learn/jvm/Test
   #6 = Methodref          #5.#45         //  com/learn/jvm/Test."<init>":()V
   #7 = Methodref          #5.#50         //  com/learn/jvm/Test.setX:(I)V
   #8 = Methodref          #51.#52        //  java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #9 = Fieldref           #5.#53         //  com/learn/jvm/Test.num:Ljava/lang/Integer;
  #10 = Fieldref           #54.#55        //  java/lang/System.out:Ljava/io/PrintStream;
  #11 = String             #56            //  hello world
  #12 = Methodref          #57.#58        //  java/io/PrintStream.println:(Ljava/lang/String;)V
  #13 = Class              #59            //  java/lang/Object
  #14 = Utf8               str
  #15 = Utf8               Ljava/lang/String;
  #16 = Utf8               x
  #17 = Utf8               I
  #18 = Utf8               num
  #19 = Utf8               Ljava/lang/Integer;
  #20 = Utf8               <init>
  #21 = Utf8               ()V
  #22 = Utf8               Code
  #23 = Utf8               LineNumberTable
  #24 = Utf8               LocalVariableTable
  #25 = Utf8               this
  #26 = Utf8               Lcom/learn/jvm/Test;
  #27 = Utf8               main
  #28 = Utf8               ([Ljava/lang/String;)V
  #29 = Utf8               args
  #30 = Utf8               [Ljava/lang/String;
  #31 = Utf8               test
  #32 = Utf8               setX
  #33 = Utf8               (I)V
  #34 = Utf8               test1
  #35 = Utf8               (Ljava/lang/String;)V
  #36 = Utf8               StackMapTable
  #37 = Class              #49            //  com/learn/jvm/Test
  #38 = Class              #60            //  java/lang/String
  #39 = Class              #59            //  java/lang/Object
  #40 = Class              #61            //  java/lang/Throwable
  #41 = Utf8               test2
  #42 = Utf8               <clinit>
  #43 = Utf8               SourceFile
  #44 = Utf8               Test.java
  #45 = NameAndType        #20:#21        //  "<init>":()V
  #46 = Utf8               Welcome
  #47 = NameAndType        #14:#15        //  str:Ljava/lang/String;
  #48 = NameAndType        #16:#17        //  x:I
  #49 = Utf8               com/learn/jvm/Test
  #50 = NameAndType        #32:#33        //  setX:(I)V
  #51 = Class              #62            //  java/lang/Integer
  #52 = NameAndType        #63:#64        //  valueOf:(I)Ljava/lang/Integer;
  #53 = NameAndType        #18:#19        //  num:Ljava/lang/Integer;
  #54 = Class              #65            //  java/lang/System
  #55 = NameAndType        #66:#67        //  out:Ljava/io/PrintStream;
  #56 = Utf8               hello world
  #57 = Class              #68            //  java/io/PrintStream
  #58 = NameAndType        #69:#35        //  println:(Ljava/lang/String;)V
  #59 = Utf8               java/lang/Object
  #60 = Utf8               java/lang/String
  #61 = Utf8               java/lang/Throwable
  #62 = Utf8               java/lang/Integer
  #63 = Utf8               valueOf
  #64 = Utf8               (I)Ljava/lang/Integer;
  #65 = Utf8               java/lang/System
  #66 = Utf8               out
  #67 = Utf8               Ljava/io/PrintStream;
  #68 = Utf8               java/io/PrintStream
  #69 = Utf8               println
{
  java.lang.String str;
    flags:

  private int x;
    flags: ACC_PRIVATE

  public static java.lang.Integer num;
    flags: ACC_PUBLIC, ACC_STATIC

  public com.learn.jvm.Test();
    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 Welcome
         7: putfield      #3                  // Field str:Ljava/lang/String;
        10: aload_0
        11: iconst_5
        12: putfield      #4                  // Field x:I
        15: return
      LineNumberTable:
        line 8: 0
        line 9: 4
        line 10: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      16     0  this   Lcom/learn/jvm/Test;

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #5                  // class com/learn/jvm/Test
         3: dup
         4: invokespecial #6                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: bipush        8
        11: invokespecial #7                  // Method setX:(I)V
        14: bipush        20
        16: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        19: putstatic     #9                  // Field num:Ljava/lang/Integer;
        22: return
      LineNumberTable:
        line 14: 0
        line 15: 8
        line 16: 14
        line 17: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      23     0  args   [Ljava/lang/String;
               8      15     1  test   Lcom/learn/jvm/Test;

  private synchronized void setX(int);
    flags: ACC_PRIVATE, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #4                  // Field x:I
         5: return
      LineNumberTable:
        line 20: 0
        line 21: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       6     0  this   Lcom/learn/jvm/Test;
               0       6     1     x   I

  private void test1(java.lang.String);
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=4, args_size=2
         0: aload_1
         1: dup
         2: astore_2
         3: monitorenter
         4: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #11                 // String hello world
         9: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_2
        13: monitorexit
        14: goto          22
        17: astore_3
        18: aload_2
        19: monitorexit
        20: aload_3
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 26: 0
        line 27: 4
        line 28: 12
        line 29: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      23     0  this   Lcom/learn/jvm/Test;
               0      23     1   str   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
           frame_type = 255 /* full_frame */
          offset_delta = 17
          locals = [ class com/learn/jvm/Test, class java/lang/String, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
           frame_type = 250 /* chop */
          offset_delta = 4


  private static synchronized void test2();
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 31: 0

  static {};
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        10
         2: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         5: putstatic     #9                  // Field num:Ljava/lang/Integer;
         8: return
      LineNumberTable:
        line 11: 0
}

 同时贴出Java字节码整体结构图便于对比查看

以下开始逐个字节进行分析(仅分析一部分重要字节和代表性的字节,因为太多了)

第1~4个字节:ca fe ba be 为魔数

第5~6个字节:00 00 代表 minor version ,值为0

第7~8个字节:00 33 代表 major version ,值为51

第9~10个字节:00 46 ,换算成十进制为 4*16+6 = 70 ,代表常量池中共有69个常量(除去一个为null保留的常量),我们通过查看上面 avap -verbose -p打印出来的信息可以验证,确实是这样的

接下来是具体的常量分析,所以将常量池中的数据类型结构图,和相关知识要点贴出来

要点:

  1. 使用java -verbose 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息
  2. 魔数:所有.class字节码文件的前四个字节都是魔数,,魔数值为固定值:0xCAFEBABE
  3. 魔数之后的4个字节代码版本号,前2个字节代表次版本号(minor version),后2个字节代表主版本号(major version),以上图为例,次版本号为0,主版本号为52,所以,该文件的版本号是1.8.0
  4. 常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个Java类定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法与变量信息,都是存储在常量池中。常量池中主要存储两类常量:字面量与符号应用。字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等
  5. 常量池的总体结构:Java类所对应的常量池主要由“常量池数量”与“常量池数组”这两部分共同构成。常量池数量紧跟在主版本号后面(也就是第9个字节开始),占据2个字节;常量池数组则紧跟在常量池数量之后。常量池数组与一般的数组不同的是,常量池数组中不同的原素的类型、结构都是不同的,长度当然也就不同,但是,每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。以上图为例,第9到第10个字节连起来时 0018,即十进制数字的24,意即后面有24个常量。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池”的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量池数组(也叫常量表)中,这个常量就对应null值,所以常量池的索引从1而非0开始。以上图为例显示有24个常量,但是根据 java -verbose的输出结果,显示只有23个常量。
  6. 在JVM规范种,每个变量/字段都有描述信息,描述信息的主要作用是描述字段的数据类型、方法的参数列表(包括数量、类型与顺序)与返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,而对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母,如下所示:B - byte ,C - char,D - double,F - float, I - int,J - long,S - short,Z - boolean,V - void,L - 对象类型,如Ljava/lang/String;
  7. 对于数组类型来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I,String[][]被记录为[[Ljava/lang/String;
  8. 用描述符来描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之类,如方法String getRealNameByIdAndName(int id, String name),表示为(I,Ljava/lang/String;)Ljava/lang/String;

(以下为第1个常量)

第11个字节:0a,换算成十进制为 10,代表该常量是上图中的 CONSTANT_Methodref_info 类型

第12~13个字节(表示这个方法时由哪个类所定义的):00 0d,换算成十进制为13,根据本文开头javap -verbose -p 的结果,最终可以看到这个索引项指向一个Class类型,值为 "java/lang/Object"

第14~15个字节(对方法本身的描述):00 2d,换算成十进制为45,从常量池中找到结果为 "<init>":()V ,即该方法名称为<init> (注:也就是构造方法),参数列表为空,返回值类型为void

(以下为第2个常量)

第16个字节:08,代表上图中的 CONSTANT_String_info 类型

第17~第18个字节:00 2e,十进制为46,引用的是字符串常量"Welcome"

(以下为第3个常量)

第19个字节:09,代表上图中的 CONSTANT_Fieldref_info 类型

第20~21个字节:00 05,引用值为 "com/learn/jvm/Test",表示该字段是在Test类中声明的

第22~23个字节:00 2f,十进制为47,值为 str:Ljava/lang/String; 表示该字段名为str,类型是一个String对象

(以下为第4个常量)

第24个字节:09,和第三个常量类似,此处省略

(以下为第5个常量)

第29个字节:07,代表上图中的 CONSTANT_Class_info 类型

第30~31个字节:00 31,十进制为49,引用值为 "com/learn/jvm/Test"

(以下为第6个常量)

第32个字节:0a,与第1个常量类似,此处省略

(以下为第7个常量)

第37个字节:0a,省略

(以下为第8个变量)

第42个字节:0a,是定义在java/lang/Integer类中的方法,方法名为 valueOf,参数列表为int,返回一个Ljava/lang/Integer对象。之所以存在这个方法,是因为我们在程序中将一个int值赋给了Integer,导致自动装箱。

(以下为第9个变量)

第47个字节:09,省略

······

 常量池到此结束

Access_Flag访问标志部分

访问标志信息包括该Class文件是类还是接口,是否被定义为public,是否是abstract,如果是类,是否被声明为final

上图缺少了0x0002代表 ACC_PRIVATE

(以下为Access Flags)

第??~??个字节:00 21 ,对应于上图就是 ACC_PUBLIC | ACC_SUPER (即这两者的并集)

The Class Name

第??~??个字节:00 05,对应常量池中索引为5的"com/learn/jvm/Test"

Super Class Name

第??~??个字节:00 0d,对应常量池中的索引为13的"java/lang/Object"

Interfaces

第??~??个字节:00 00,代表接口数量为0,我们的类没有实现任何接口

Fields (2+N个字节)

接下就到了字段表集合了,相关知识如下:

字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。

上面两张图是一个意思,都是介绍了字段表集合的结构

第??~??个字节:00 03,代表有3个成员变量

(以下为第1个成员变量)

access_flags 为 00 00 ,代表默认访问级别(即 "default")

name_index 为 00 0e,即常量池中索引值为14指向的常量  "str"

descriptor_index 为 00 0f,在常量池中找到为 "Ljava/lang/String;"

attributes_count 为 00 00,属性数量为0,表示没有属性 (备注:属性是什么??)

由于属性数量为0,attribute_info也就没有了

(以下为第2个成员变量)

0002 ,代表访问修饰符为 private

0010,在常量池中找到值为 "x" ,表示变量名为 x

0011,在常量池中找到值为 "I",表明这个成员变量的类型为 int

0000,表示属性数量为0

(以下为第三个成员变量)

0009,代表访问修饰符为 public static (上图中找不到0009的解释,但是在jclasslib插件中可以看到是public static)

0012,在常量池中找到为num,表示变量名为num

0013,在常量池找到为  Ljava/lang/Integer;  表示变量类型

0000,属性数量为0

Method  部分 ,前两个字节表示方法数量

0006,表示有6个方法,从下图jclasslib的结果中也可以看出确实是6个,其中最后一个方法是对静态信息的初始化,包括静态的成员变量和静态块

下面开始逐个分析这些方法的十六进制代码,先贴出方法表相关的知识:

 其中 attribute_info,这个属性跟方法里面的成员变量不是一回事,这个属性是归属于这个方法本身的。javac在编译好一个字节码文件之后,会给相应的方法额外增加一些attribute信息,这些attribute信息就描述了这个方法,比如方法的执行字节码是什么,方法的行号表是什么,局部变量表是什么,attribute_count就表明这些属性一共有多少个。attribute_info结构如下

注意,上图中的 u1 info[attribute_length]其实放在这里这种写法是很容易造成混淆的,其实它的意思仅仅是表示有attribute_length个info,每个info是一个字节。我在这里晕了半天,搞懂了,所以记录一下。

 

注意,其中 u1 code[code_length]表示的是一共有code_length个code,每个code是1个字节。据里来说,u4 code_length的值是10,那么说明接下来10个字节是虚拟机真正执行的质量,即助记符,u1 code[10]就表示这个意思。这张图这个地方是很容易造成误解的,所以我理解之后做个这个备注进行说明。

 

 

(第一个方法)

0001:代表访问修饰符为 ACC_PUBLIC

0014:方法名字索引,十进制为20,在常量池中找到为<init>

0015:方法描述符,十进制为21,在常量池中找到为()V,表示参数个数为0,返回类型为void

0001:表示属性个数为1个

(以下为第1个方法的attribute_info)

 0016:属性名,十进制为22,在常量池中找到值为"Code",表明从这里开始,是一个Code类型的属性,Code的结构参考上面的几张图

00000042:属性长度,表示一共66个字节,即接下来66个字节就是这个方法真正所执行的代码对应的指令

(以下为第1个方法的attribute_info的Code属性(即Code_attribute除去名字和长度字段的内容)的内容)

0002:(max_stack) ,最大操作数栈深度为2

0001:(max_locals),

00000010:(code_length),接下来16个字节就是该方法对应的真正的虚拟机将要执行的指令,这些十六进制代码对应了各自的助记符

-----------------------------------------------------------------------------------------------

手工翻译的工作暂时到此为止吧,有点单调乏味,下面用jclasslib插件来看其他一些需要学习的点

-----------------------------------------------------------------------------------------------

1. 构造函数

从上图中可以看出,实际上我们对str和x这两个变量的赋值是在自动生成的构造方法里完成的。这就有一个疑问,上面这种情况是在我们没有自定义构造函数的情况下发生的,如果我们定义了构造函数,情况又将如何?

  可以看出,编译器将x和str的赋值合并到了我们自定义的构造方法里面

如果我们有两个构造方法,又是什么情况呢?

 

 试了一下,结果发现如上图,生成的字节码中,两个构造函数里均有对x和str赋值的相关代码

2.关于锁

先逐个解释这些助记符(助记符文档 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.dup)

aload_1:将局部变量表中的1号变量的值压入栈顶,即将str变量压入栈顶,注意,0号参数是this,所以str只能是1号变量

dup:复制操作数堆栈上的顶部值,并将复制 的值压入操作数堆栈。

先解释到这里吧,感觉还需要学习。

原文地址:https://www.cnblogs.com/heben/p/11468285.html