JVM学习笔记(15) --Class文件结构

可参考思维导图
jvm规范


一. 概述

1.1 字节码文件的跨平台性

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

1.2 java的前端编译器

在这里插入图片描述

AoT 在代码执行之前就将代码编译成机器指令,运行效率稍微高点,但是就牺牲了java 的动态运行的特性, 影响了一次编译到处运行

在这里插入图片描述

1.3 透过字节码指令看代码细节

在这里插入图片描述

代码举例1

java代码
public class IntegerTest {
    public static void main(String[] args) {

        Integer x = 5;
        int y = 5;
        // true
        System.out.println(x == y);
        
        Integer i1 = 10;
        Integer i2 = 10;
        // true
        System.out.println(i1 == i2);

        Integer i3 = 128;
        Integer i4 = 128;
        // false
        System.out.println(i3 == i4);
    }
}
字节码文件

1行 --> 调用调用了valueof 方法, 实际是首先初始化了一个256大小(byte) 的new Integer()[], 然后进行下面的判断

   public static Integer valueOf(int i) {
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
   }

如果数据是在-128 - 127之间的话是直接从数组中取出数据的,没有新new对象

 0 iconst_5  									# 将5 放入操作数栈中
 1 invokestatic #2 <java/lang/Integer.valueOf>  # 调用valueOf()方法
 4 astore_1									    # 取出栈中的数据 5 放到局部变量表位置为1(x变量) 的地方, 0的位置放的是args变量
 5 iconst_5										# 将5放入操作数栈中
 6 istore_2										# 将栈中的5取出放入局部变量表为2(y变量)的地方
 7 getstatic #3 <java/lang/System.out>
10 aload_1
11 invokevirtual #4 <java/lang/Integer.intValue>
14 iload_2
15 if_icmpne 22 (+7)
18 iconst_1
19 goto 23 (+4)
22 iconst_0
23 invokevirtual #5 <java/io/PrintStream.println>
26 bipush 10
28 invokestatic #2 <java/lang/Integer.valueOf>
31 astore_3
32 bipush 10
34 invokestatic #2 <java/lang/Integer.valueOf>
37 astore 4
39 getstatic #3 <java/lang/System.out>
42 aload_3
43 aload 4
45 if_acmpne 52 (+7)
48 iconst_1
49 goto 53 (+4)
52 iconst_0
53 invokevirtual #5 <java/io/PrintStream.println>
56 sipush 128
59 invokestatic #2 <java/lang/Integer.valueOf>
62 astore 5
64 sipush 128
67 invokestatic #2 <java/lang/Integer.valueOf>
70 astore 6
72 getstatic #3 <java/lang/System.out>
75 aload 5
77 aload 6
79 if_acmpne 86 (+7)
82 iconst_1
83 goto 87 (+4)
86 iconst_0
87 invokevirtual #5 <java/io/PrintStream.println>
90 return

代码举例2

java 代码
public class StringTest {
    public static void main(String[] args) {
        String str = new String("hello") + new String("World");
        // str.intern();
        String str1 = "helloWorld";
        // false   // 在jdk8.0如果调用了intern()方法,则变成了true
        System.out.println(str == str1);
    }
}
字节码
 0 new #2 <java/lang/StringBuilder>
 3 dup
 4 invokespecial #3 <java/lang/StringBuilder.<init>>
 7 new #4 <java/lang/String>
10 dup
11 ldc #5 <hello>
13 invokespecial #6 <java/lang/String.<init>>
16 invokevirtual #7 <java/lang/StringBuilder.append>
19 new #4 <java/lang/String>
22 dup
23 ldc #8 <World>
25 invokespecial #6 <java/lang/String.<init>>
28 invokevirtual #7 <java/lang/StringBuilder.append>
31 invokevirtual #9 <java/lang/StringBuilder.toString>
34 astore_1
35 ldc #10 <helloWorld>
37 astore_2
38 getstatic #11 <java/lang/System.out>
41 aload_1
42 aload_2
43 if_acmpne 50 (+7)
46 iconst_1
47 goto 51 (+4)
50 iconst_0
51 invokevirtual #12 <java/io/PrintStream.println>
54 return

代码举例3

成员变量(非静态的) 的赋值过程: 1> 默认初始化 2> 显示初始化 / 代码块中初始化 3> 构造器中初始化 4> 有了对象之后, 可"对象.属性"或者"对象.方法"初始化

Java代码
class Father {
    int x = 10;
    public Father() {
        this.print();
        x = 20;
    }

    public void print() {
        System.out.println("Father.x = " + x);
    }
}

class Son extends Father {
    int x = 30;
    public Son() {
        this.print();
        x = 40;
    }

    @Override
    public void print() {
        System.out.println("Son.x = " + x);
    }
}

public class SonTest {
    public static void main(String[] args) {
        Father f = new Son();
        System.out.println(f.x);
    }
    /**
     * Son.x = 0
     * Son.x = 30
     * 20
     */
}

首先打印Son.x = 0 的原因是在调son的构造函数之前首先调用了父类的构造,在父类的构造里面调用了this.print, 因为子类重写了父类的print方法,这个时候实际调用的是子类的print, 其实这个时候子类中的x并没有被初始化为30,还是为0, 所以打印的是一个0
然后是调用子类构造方法,此时子类中的x已经被初始化成了30, 所以打印Son.x = 30
然后是父类.属性,由于属性并不存在多态性, 所以打印的是父类自己的属性 20

字节码
main
 0 new #2 <demo6/Son>
 3 dup
 4 invokespecial #3 <demo6/Son.<init>>
 7 astore_1
 8 getstatic #4 <java/lang/System.out>
11 aload_1
12 getfield #5 <demo6/Father.x>
15 invokevirtual #6 <java/io/PrintStream.println>
18 return

son
 0 aload_0
 1 invokespecial #1 <demo6/Father.<init>>
 4 aload_0
 5 bipush 30
 7 putfield #2 <demo6/Son.x>
10 aload_0
11 invokevirtual #3 <demo6/Son.print>
14 aload_0
15 bipush 40
17 putfield #2 <demo6/Son.x>
20 return

father
 0 aload_0
 1 invokespecial #1 <java/lang/Object.<init>>
 4 aload_0
 5 bipush 10
 7 putfield #2 <demo6/Father.x>
10 aload_0
11 invokevirtual #3 <demo6/Father.print>
14 aload_0
15 bipush 20
17 putfield #2 <demo6/Father.x>
20 return


二. 虚拟机的基石 : Class文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


三.Class文件结构

可以把表理解为数组结构
在这里插入图片描述

示例:

代码
public class Demo {
    private int num = 1;

    public int add() {
        num = num + 2;
        return num;
    }
}
反编译得到的代码
package demo7;

public class Demo {
    private int num = 1;

    public Demo() {
    }

    public int add() {
        this.num += 2;
        return this.num;
    }
}
字节码文件
// class version 52.0 (52)
// access flags 0x21
public class demo7/Demo {

  // compiled from: Demo.java

  // access flags 0x2
  private I num

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 11 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 12 L1
    ALOAD 0
    ICONST_1
    PUTFIELD demo7/Demo.num : I
    RETURN
   L2
    LOCALVARIABLE this Ldemo7/Demo; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x1
  public add()I
   L0
    LINENUMBER 15 L0
    ALOAD 0
    ALOAD 0
    GETFIELD demo7/Demo.num : I
    ICONST_2
    IADD
    PUTFIELD demo7/Demo.num : I
   L1
    LINENUMBER 16 L1
    ALOAD 0
    GETFIELD demo7/Demo.num : I
    IRETURN
   L2
    LOCALVARIABLE this Ldemo7/Demo; L0 L2 0
    MAXSTACK = 3
    MAXLOCALS = 1
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.1 魔数 : Class文件的标志

在这里插入图片描述

3.2 Class文件版本号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 常量池: 存放所有常量

在这里插入图片描述

运行时常量池中存放的是各种字面量符号引用
在这里插入图片描述

引用资料
在这里插入图片描述

3.3.1 常量池计数器

在这里插入图片描述

3.3.2 常量池表

在这里插入图片描述
在这里插入图片描述

3.3.2.1 字面量和符号引用

注: 此时数据并未在内存中,只是存在于class字节码文件中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3.2.2 常量类型和结构

可结合前面的excel文件查看解析结果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4 访问标识

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5 类索引, 父类索引 ,接口索引集合

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.6 字段(field)表集合

在这里插入图片描述

3.6.1 字段计数器

在这里插入图片描述

3.6.2 字段表

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.7 方法表集合

在这里插入图片描述

3.7.1 方法计数器

在这里插入图片描述

3.7.2 方法表

在这里插入图片描述
在这里插入图片描述

3.8 属性表集合

在这里插入图片描述

3.8.1 属性计数器

在这里插入图片描述

3.8.2 属性表

在这里插入图片描述
在这里插入图片描述

具体参看, 纯字节码的看看得了

3.9 小结

在这里插入图片描述


四.使用Javap指令解析Class文件

4.1解析字节码的作用

在这里插入图片描述

4.2javac -g 操作

在这里插入图片描述

4.3 javap的用法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 使用举例

java

public class JavapTest {
    private int num;
    boolean flag;
    protected char gender;
    public String info;

    public static final int COUNTS = 1;

    static {
        String url = "www.huqi.com";
    }
    {
        info = "java";
    }
    public JavapTest() {

    }
    private JavapTest(boolean flag) {
        this.flag = flag;
    }
    private void methodPrivate() {

    }
    int getNum(int i){
        return num + i;
    }
    protected char showGender() {
        return gender;
    }
    public void showInfo() {
        int i = 10;
        System.out.println(info + i);
    }
}

字节码

javap -v -p JavapTest.class 拿到所有的完整的信息

 Classfile /D:/code/demo/JVMDemo/ch03/src/main/java/demo7/JavapTest.class  // 字节码文件所属的路径 
  Last modified 2021-5-11; size 1331 bytes								   // 最后修改时间, 字节码文件大小
  MD5 checksum 45db889d416b756de5fa084e44259081								// MD5散列值
  Compiled from "JavapTest.java"											// 源文件的名称
public class demo7.JavapTest
  minor version: 0															// 副版本
  major version: 52															// 主版本
  flags: ACC_PUBLIC, ACC_SUPER												// 访问标识. ACC_SUPER代表这是一个类, public代表是公开的		
Constant pool:																// 常量池
   #1 = Methodref          #16.#46        // java/lang/Object."<init>":()V
   #2 = String             #47            // java
   #3 = Fieldref           #15.#48        // demo7/JavapTest.info:Ljava/lang/String;
   #4 = Fieldref           #15.#49        // demo7/JavapTest.flag:Z
   #5 = Fieldref           #15.#50        // demo7/JavapTest.num:I
   #6 = Fieldref           #15.#51        // demo7/JavapTest.gender:C
   #7 = Fieldref           #52.#53        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Class              #54            // java/lang/StringBuilder
   #9 = Methodref          #8.#46         // java/lang/StringBuilder."<init>":()V
  #10 = Methodref          #8.#55         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = Methodref          #8.#56         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #12 = Methodref          #8.#57         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #13 = Methodref          #58.#59        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #14 = String             #60            // www.huqi.com
  #15 = Class              #61            // demo7/JavapTest
  #16 = Class              #62            // java/lang/Object
  #17 = Utf8               num
  #18 = Utf8               I
  #19 = Utf8               flag
  #20 = Utf8               Z
  #21 = Utf8               gender
  #22 = Utf8               C
  #23 = Utf8               info
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               COUNTS
  #26 = Utf8               ConstantValue
  #27 = Integer            1
  #28 = Utf8               <init>
  #29 = Utf8               ()V
  #30 = Utf8               Code
  #31 = Utf8               LineNumberTable
  #32 = Utf8               LocalVariableTable
  #33 = Utf8               this
  #34 = Utf8               Ldemo7/JavapTest;
  #35 = Utf8               (Z)V
  #36 = Utf8               methodPrivate
  #37 = Utf8               getNum
  #38 = Utf8               (I)I
  #39 = Utf8               i
  #40 = Utf8               showGender
  #41 = Utf8               ()C
  #42 = Utf8               showInfo
  #43 = Utf8               <clinit>
  #44 = Utf8               SourceFile
  #45 = Utf8               JavapTest.java
  #46 = NameAndType        #28:#29        // "<init>":()V
  #47 = Utf8               java
  #48 = NameAndType        #23:#24        // info:Ljava/lang/String;
  #49 = NameAndType        #19:#20        // flag:Z
  #50 = NameAndType        #17:#18        // num:I
  #51 = NameAndType        #21:#22        // gender:C
  #52 = Class              #63            // java/lang/System
  #53 = NameAndType        #64:#65        // out:Ljava/io/PrintStream;
  #54 = Utf8               java/lang/StringBuilder
  #55 = NameAndType        #66:#67        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #56 = NameAndType        #66:#68        // append:(I)Ljava/lang/StringBuilder;
  #57 = NameAndType        #69:#70        // toString:()Ljava/lang/String;
  #58 = Class              #71            // java/io/PrintStream
  #59 = NameAndType        #72:#73        // println:(Ljava/lang/String;)V
  #60 = Utf8               www.huqi.com
  #61 = Utf8               demo7/JavapTest
  #62 = Utf8               java/lang/Object
  #63 = Utf8               java/lang/System
  #64 = Utf8               out
  #65 = Utf8               Ljava/io/PrintStream;
  #66 = Utf8               append
  #67 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #68 = Utf8               (I)Ljava/lang/StringBuilder;
  #69 = Utf8               toString
  #70 = Utf8               ()Ljava/lang/String;
  #71 = Utf8               java/io/PrintStream
  #72 = Utf8               println
  #73 = Utf8               (Ljava/lang/String;)V
  ################################# 字段表集合的信息 ####################################################################
{														// 字段表集合的信息
  private int num;										// 字段名
    descriptor: I										// 字段描述符: 字段的类型
    flags: ACC_PRIVATE									// 字段的访问标识

  boolean flag;
    descriptor: Z
    flags:

  protected char gender;
    descriptor: C
    flags: ACC_PROTECTED

  public java.lang.String info;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC

  public static final int COUNTS;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 1    							// 常量字段的属性: ConstantValue

################################### 方法表集合的信息 ##################################################################
  public demo7.JavapTest(); 							// 构造器1的信息
    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 java
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 21: 0
        line 19: 4
        line 23: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Ldemo7/JavapTest;

  private demo7.JavapTest(boolean);					// 构造器2的信息
    descriptor: (Z)V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String java
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: aload_0
        11: iload_1
        12: putfield      #4                  // Field flag:Z
        15: return
      LineNumberTable:
        line 24: 0
        line 19: 4
        line 25: 10
        line 26: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Ldemo7/JavapTest;
            0      16     1  flag   Z

  private void methodPrivate();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 29: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   Ldemo7/JavapTest;

  int getNum(int);
    descriptor: (I)I
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: getfield      #5                  // Field num:I
         4: iload_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 31: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Ldemo7/JavapTest;
            0       7     1     i   I

  protected char showGender();
    descriptor: ()C
    flags: ACC_PROTECTED
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #6                  // Field gender:C
         4: ireturn
      LineNumberTable:
        line 34: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ldemo7/JavapTest;

  public void showInfo();
    descriptor: ()V									// 方法描述符: 方法的形参列表,返回值类型
    flags: ACC_PUBLIC								// 方法的访问标识
    Code:
      stack=3, locals=2, args_size=1	 // stack: 操作数栈的最大深度  locals: 局部变量表的长度(this + 自己定义的一个)  args_size: 方法接受参数的个数(无参的都为1,是this)
   	//偏移量  操作码       操作数
         0: bipush        10
         2: istore_1
         3: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #8                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        13: aload_0
        14: getfield      #3                  // Field info:Ljava/lang/String;
        17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: iload_1
        21: invokevirtual #11                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        24: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: return
     // 行号表: 指明字节码指令的偏移量于java源程序中代码的行号的一一对应关系
      LineNumberTable:
        line 37: 0     java代码中的行号(37)   :   字节码指令中的行号(0)
        line 38: 3
        line 39: 30
     // 局部变量表
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  this   Ldemo7/JavapTest;0 - 31 行有效
            3      28     1     i   I

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: ldc           #14                 // String www.huqi.com
         2: astore_0
         3: return
      LineNumberTable:
        line 16: 0
        line 17: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}
SourceFile: "JavapTest.java"   				// 附加属性: 指明当前字节码文件对应的源程序文件名

4.5 总结

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值