符号引用:
符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
直接引用:
直接引用可以是
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
(3)一个能间接定位到目标的句柄
直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。
符号引用的意思我理解成就是一个大概为jvm描述一些引用目标,采用了一些符号来表示。 暂时先这么理解
下面是我的一些分析方式
javap命令是jdk自带的一个反汇编的工具,可以把class文件反编译成汇编的命令,查看一些java执行的一些内部指令。
我一般使用的 javap -c -l -p -v A.class
直接上代码
package com.jvm.classloading;
public class B {
public static void main(String[] args) {
}
public int b1(int a,int b){
int c = b2(a,b);
return c;
}
public int b2(int a,int b){
return a+b;
}
}
使用javap解析为
Classfile /C:/Users/able/IdeaProjects/jvm/src/com/jvm/classloading/B.class
Last modified 2020-4-4; size 389 bytes
MD5 checksum 7ea5d05de8809bd5775b1195eb296819
Compiled from "B.java"
public class com.jvm.classloading.B
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#16 // java/lang/Object."<init>":()V
#2 = Methodref #3.#17 // com/jvm/classloading/B.b2:(II)I
#3 = Class #18 // com/jvm/classloading/B
#4 = Class #19 // 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 b1
#12 = Utf8 (II)I
#13 = Utf8 b2
#14 = Utf8 SourceFile
#15 = Utf8 B.java
#16 = NameAndType #5:#6 // "<init>":()V
#17 = NameAndType #13:#12 // b2:(II)I
#18 = Utf8 com/jvm/classloading/B
#19 = Utf8 java/lang/Object
{
public com.jvm.classloading.B();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 7: 0
public int b1(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=3, locals=4, args_size=3
0: aload_0
1: iload_1
2: iload_2
3: invokevirtual #2 // Method b2:(II)I
6: istore_3
7: iload_3
8: ireturn
LineNumberTable:
line 10: 0
line 11: 7
public int b2(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: iadd
3: ireturn
LineNumberTable:
line 15: 0
}
SourceFile: "B.java"
首先我们看b1 这个方法, b1 方法调用了b2,看下b1是如何调用b2,如何解析的
1 aload_0, 2 iload_1 , 3 iload_2 这三个jvm字节解释出来,把参数1和参数2放在栈中.
主要看下
3: invokevirtual #2 // Method b2:(II)I
invokevirtual 的意思是调用实列方法,那么这里调用那个呢? 注意看下 后面有个#2,我们找到javap的解析,发现如下
#2 = Methodref #3.#17 // com/jvm/classloading/B.b2:(II)I
发现#2 = #3.#17 注意 不要看掉. 我们又分别看下 #3 和#17
#3 = Class #18 // com/jvm/classloading/B
#17 = NameAndType #13:#12 // b2:(II)I
发现 后面还有带有#的,真多, 先看#18
#18 = Utf8 com/jvm/classloading/B
终于找到了,#18 代表的是我这个类,class的路劲
在看#17
#12 = Utf8 (II)I
#13 = Utf8 b2
按照#17的组装 得到 b2:(II)I 根据代码结合出来,小括号里面是两个参数,小括号的I是返回参数
在根据#2一起组装起来 com/jvm/classloadfing/B.b2(II)I;
在#2哪里看到注释,已经把这里给解析出来了,我们只不过顺着顺序,找出来句子.
从这里就可以看出来 符号引用使用了#2来表示b2方法的调用. 这里可以理解成#2就是符号引用。
我现在有一个问题, 为什么要用符号引用,直接用直接引用,还没有这么复杂。
后续待完善.