动态链接(Dynamic Linking)(指向运行时常量池的方法的引用)
- 每一个栈帧内部都包含一个指向运行时常量池中该帧所属方法的引用。包含这个引用的目的是为了支持当前方法的代码能实现动态链接(Dynamic Linking)。
- 在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用另外一个方法时,是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用的直接引用
示例
如下代码,我们创建一个类DynamicLinkingTest。声明一个int型的变量num,methodA( )和methodB( ) ,methodB( )中调用了methodA( )然后执行num++操作
public class DynamicLinkingTest {
int num=10;
public void methodA(){
System.out.println("methodA....");
}
public void methodB(){
System.out.println("methodB....");
methodA();
num++;
}
}
然后我们通过javac 编译DynamicLinkingTest.java生成DynamicLinkingTest.class。在然后通过javap -verbose 将DynamicLinkingTest.class反编译得到如下:
Last modified 2021-4-26; size 582 bytes
MD5 checksum a16ed43381b08c46ce7d9518e43519bc
Compiled from "DynamicLinkingTest.java"
public class com.booyue.tlh.DynamicLinkingTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #8.#21 // com/booyue/tlh/DynamicLinkingTest.num:I
#3 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#4 = String #24 // methodA....
#5 = Methodref #25.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
#6 = String #27 // methodB....
#7 = Methodref #8.#28 // com/booyue/tlh/DynamicLinkingTest.methodA:()V
#8 = Class #29 // com/booyue/tlh/DynamicLinkingTest
#9 = Class #30 // java/lang/Object
#10 = Utf8 num
#11 = Utf8 I
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 methodA
#17 = Utf8 methodB
#18 = Utf8 SourceFile
#19 = Utf8 DynamicLinkingTest.java
#20 = NameAndType #12:#13 // "<init>":()V
#21 = NameAndType #10:#11 // num:I
#22 = Class #31 // java/lang/System
#23 = NameAndType #32:#33 // out:Ljava/io/PrintStream;
#24 = Utf8 methodA....
#25 = Class #34 // java/io/PrintStream
#26 = NameAndType #35:#36 // println:(Ljava/lang/String;)V
#27 = Utf8 methodB....
#28 = NameAndType #16:#13 // methodA:()V
#29 = Utf8 com/booyue/tlh/DynamicLinkingTest
#30 = Utf8 java/lang/Object
#31 = Utf8 java/lang/System
#32 = Utf8 out
#33 = Utf8 Ljava/io/PrintStream;
#34 = Utf8 java/io/PrintStream
#35 = Utf8 println
#36 = Utf8 (Ljava/lang/String;)V
{
int num;
descriptor: I
flags:
public com.booyue.tlh.DynamicLinkingTest();
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: bipush 10
7: putfield #2 // Field num:I
10: return
LineNumberTable:
line 3: 0
line 5: 4
public void methodA();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String methodA....
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 8: 0
line 9: 8
public void methodB();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String methodB....
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: aload_0
9: invokevirtual #7 // Method methodA:()V
12: aload_0
13: dup
14: getfield #2 // Field num:I
17: iconst_1
18: iadd
19: putfield #2 // Field num:I
22: return
LineNumberTable:
line 12: 0
line 13: 8
line 14: 12
line 15: 22
}
从上往下看我们找到到Constant pool和methodB( )方法,然后在methodB( )中找到 9: invokevirtual #7。
invokevirtual后面是#7,我们从 Constant pool(常量池)中找到#7,发现#7对应的是 #8.#28 ,我们在找到#8,然后#8对应的是#29,在找到#29,发现#29对应的是: com/booyue/tlh/DynamicLinkingTest。然后在找#28,发现#28对应的是#16:#13,我们在找到#16和#13,#16对应的是methodA,#13是( )V,这里#16和#13说明了methodA方法是一个没有返回值的方法。然后把我们刚刚从#8.#28串联起来就得到:com/booyue/tlh/DynamicLinkingTest类中的没有返回值的methodA方法了。
另外我们在methodB( )中还调用了num变量,按照上面的方法,你就能找出num是一个int类型的变量。
图例