JVM符号引用暴力理解

关于JVM中符号引用的理解,小白专场。。。。

public class Tests10 {

	public static void main(String[] args) {
		Sum sum = new Sum();
        int a = 40;
        int b = 60;
        int c = sum.add(a,b);
        System.out.println(c);
	}

}

class Sum {
    public int add(int a,int b) {
        return a + b;
    }
}

源文件编译后的.class文件反编译后的类似汇编语言的字节码文件:

Constant pool:
   #1 = Class              #2             // Tests10
   #2 = Utf8               Tests10
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LTests10;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Class              #17            // Sum
  #17 = Utf8               Sum
  #18 = Methodref          #16.#9         // Sum."<init>":()V
  #19 = Methodref          #16.#20        // Sum.add:(II)I
  #20 = NameAndType        #21:#22        // add:(II)I
  #21 = Utf8               add
  #22 = Utf8               (II)I
  #23 = Fieldref           #24.#26        // java/lang/System.out:Ljava/io/PrintStream;
  #24 = Class              #25            // java/lang/System
  #25 = Utf8               java/lang/System
  #26 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Methodref          #30.#32        // java/io/PrintStream.println:(I)V
  #30 = Class              #31            // java/io/PrintStream
  #31 = Utf8               java/io/PrintStream
  #32 = NameAndType        #33:#34        // println:(I)V
  #33 = Utf8               println
  #34 = Utf8               (I)V
  #35 = Utf8               args
  #36 = Utf8               [Ljava/lang/String;
  #37 = Utf8               sum
  #38 = Utf8               LSum;
  #39 = Utf8               a
  #40 = Utf8               I
  #41 = Utf8               b
  #42 = Utf8               c
  #43 = Utf8               SourceFile
  #44 = Utf8               Tests10.java
{
  public Tests10();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTests10;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: new           #16                 // class Sum
         3: dup
         4: invokespecial #18                 // Method Sum."<init>":()V
         7: astore_1
         8: bipush        40
        10: istore_2
        11: bipush        60
        13: istore_3
        14: aload_1
        15: iload_2
        16: iload_3
        17: invokevirtual #19                 // Method Sum.add:(II)I
        20: istore        4
        22: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
        25: iload         4
        27: invokevirtual #29                 // Method java/io/PrintStream.println:(I)V
        30: return
      LineNumberTable:
        line 5: 0
        line 6: 8
        line 7: 11
        line 8: 14
        line 9: 22
        line 10: 30
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  args   [Ljava/lang/String;
            8      23     1   sum   LSum;
           11      20     2     a   I
           14      17     3     b   I
           22       9     4     c   I
}
SourceFile: "Tests10.java"

我们对比src文件和反编译后的文件来看(两者是等价的)
我们从src文件中main方法中看到有个调用了add方法,然后回到反编译后的文件,下面invokevirtual指令是调用方法的意思,后面的#18是字符串常量池的下标。

4: invokespecial #18                 // Method Sum."<init>":()V

我们定位到#18这个地方,就得到了这个,

#18 = Methodref          #16.#9         // Sum."<init>":()V

根据对虚拟机规范中对Methodref 的定义:

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

主要看这两句,class_index 表示了类的索引,name_and_type_index 表示了名称与类型的索引,这两个也都是常量池的下标,可以看成Methodref 的内容:

    u2 class_index;
    u2 name_and_type_index;

上面的内容的“地址”分别对应#18 = Methodref后面的#16.#19这两个下标,按#16.#19继续到常量池找(注意:看缩进,就像树结构一样,一层一层找,找到最后的叶子结点就是对应的方法所属类的类名 方法名和和类型),

 #16 = Class              #17            // Sum
   #17 = Utf8               Sum

 #19 = Methodref          #16.#20        // Sum.add:(II)I
   #16 = Class              #17            // Sum
      #17 = Utf8               Sum
   #20 = NameAndType        #21:#22        // add:(II)I
      #21 = Utf8               add
      #22 = Utf8               (II)I

按照此规律如此下去,最终结果如下:

#17 = Utf8               Sum
#21 = Utf8               add
#22 = Utf8               (II)I

看吧,每个字符串常量后面都有连接下一个字符串常量的地址,每个字符串常量看成一个“线索”,这些线索连起来就可以得到描述(如Sum),该例是对调用的方法的描述,它属于Sum的方法,即默认包Sum类下的方法,方法名是add,类型是(II)I。

总结:
反编译后的.class文件就是通过一个一个“线索”(字符串常量)连接起来组成得到的描述,得到的所有描述可以想象成几乎与src文件一模一样的蓝图。同时“地址”看成逻辑地址,用这个在内存中找不到对应程序片段的入口,这只是将逻辑思路连起来方便读者解读,也就是说这些只不过是以字符串的形式描述,真正变成可以到内存中找的地址是在虚拟机中落地变成直接引用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fire king

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值