20.JVM栈帧的内部结构-动态链接(Dynamic Linking)
1.动态链接(指向运行时常量池的方法引用)
1.动态链接又称为指向运行时常量池的方法引用
。
2.一个方法对应一个栈帧。每一个栈帧中都包含一个指向运行时常量池中该栈帧所属方法的引用。
3.Java源文件被编译到字节码文件中时,所有的变量
和方法
引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。例如:描述一个方法调用了另外其他的方法时,就是通过常量池中指向方法的符号引用来表示的,动态链接的作用是为了将这些符号引用转换为调用方法的直接引用。
如下图所示,每一个线程都有自己的PC计数器、本地方法栈、虚拟机栈。虚拟机栈中存储的是一个个栈帧,栈帧中包含局部变量表、方法返回地址、操作数栈、动态链接(指向运行时常量池的方法引用)。
图中Constant Pool Reference就是动态链接,指向的是方法区中运行时常量池的方法引用(method reference)。
例子:
package jvn; public class DynamicLinkingTest { int num = 10; public void methodA(){ System.out.println("methodA()...."); } public void methodB(){ System.out.println("methodB()...."); methodA(); num++; } }
使用javap -v DynamicLinkingTest
反编译上面的java代码产生的class文件。得到下面的字节码输出。
1.其中的Constant pool
就是常量池,当程序运行起来的时候,就是运行时常量池。
2.第一列#1,#2
等等,这些就是符号引用。
3.methodB调用methodA
对应的指令是9: invokevirtual #36 // Method methodA:()V
。
4.为什么需要常量池?
常量池的作用就是为了提供一些符号和常量,便于指令的识别。
C:\Users\12558>javap -v D:\Eclipse_workspace\JVM\jvn\bin\jvn\DynamicLinkingTest.class Classfile /D:/Eclipse_workspace/JVM/jvn/bin/jvn/DynamicLinkingTest.class Last modified 2020-7-5; size 688 bytes MD5 checksum 7c8d5b298eaf82e8891ad1de67e14fbd Compiled from "DynamicLinkingTest.java" public class jvn.DynamicLinkingTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Class #2 // jvn/DynamicLinkingTest #2 = Utf8 jvn/DynamicLinkingTest #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 num #6 = Utf8 I #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Methodref #3.#11 // java/lang/Object."<init>":()V #11 = NameAndType #7:#8 // "<init>":()V #12 = Fieldref #1.#13 // jvn/DynamicLinkingTest.num:I #13 = NameAndType #5:#6 // num:I #14 = Utf8 LineNumberTable #15 = Utf8 LocalVariableTable #16 = Utf8 this #17 = Utf8 Ljvn/DynamicLinkingTest; #18 = Utf8 methodA #19 = Fieldref #20.#22 // java/lang/System.out:Ljava/io/PrintStream; #20 = Class #21 // java/lang/System #21 = Utf8 java/lang/System #22 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = String #26 // methodA().... #26 = Utf8 methodA().... #27 = Methodref #28.#30 // java/io/PrintStream.println:(Ljava/lang/String;)V #28 = Class #29 // java/io/PrintStream #29 = Utf8 java/io/PrintStream #30 = NameAndType #31:#32 // println:(Ljava/lang/String;)V #31 = Utf8 println #32 = Utf8 (Ljava/lang/String;)V #33 = Utf8 methodB #34 = String #35 // methodB().... #35 = Utf8 methodB().... #36 = Methodref #1.#37 // jvn/DynamicLinkingTest.methodA:()V #37 = NameAndType #18:#8 // methodA:()V #38 = Utf8 SourceFile #39 = Utf8 DynamicLinkingTest.java { int num; descriptor: I flags: public jvn.DynamicLinkingTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: aload_0 5: bipush 10 7: putfield #12 // Field num:I 10: return LineNumberTable: line 3: 0 line 5: 4 line 3: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Ljvn/DynamicLinkingTest; public void methodA(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #25 // String methodA().... 5: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/St ring;)V 8: return LineNumberTable: line 8: 0 line 9: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Ljvn/DynamicLinkingTest; public void methodB(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #34 // String methodB().... 5: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/St ring;)V 8: aload_0 9: invokevirtual #36 // Method methodA:()V 12: aload_0 13: dup 14: getfield #12 // Field num:I 17: iconst_1 18: iadd 19: putfield #12 // Field num:I 22: return LineNumberTable: line 12: 0 line 14: 8 line 16: 12 line 17: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Ljvn/DynamicLinkingTest; } SourceFile: "DynamicLinkingTest.java"