😉😉 学习交流群:
✅✅1:这是孙哥suns给大家的福利!
✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料
🥭🥭3:QQ群:583783824 📚📚 工作微信:BigTreeJava 拉你进微信群,免费领取!
🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞
💞💞5:以上内容,进群免费领取呦~ 💞💞💞💞

文章目录
我们每天都在写方法的调用,但是我们能搞明白其中的原理和JVM当中的操作步骤么?这就是本文的意义。
官方说法:
在JVM中,将符号引用转换为调用方法的直接引用这个操作是跟JVM当中方法的绑定机制息息相关的。
说人话:
上边这段话是什么意思?我这里给大家解释一下,我们javap整理完毕字节码文件之后,我们会可以在任意一个方法中查看code下的字节码指令,很多字节码指令的后边都会跟#数字这么一个概念,这个就是符号引用,这个引用指向常量池。
所谓将符号引用转换为方法的直接引用,就是将这个字节码指令后边的符号引用,转变为真实的方法。
下列中的#3就是符号引用。
- public void methodB();
- descriptor: ()V
- flags: (0x0001) 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
从上述找一个例子的话,就是将偏移地址为9的字节码指令后边的#7这个符号引用用真实的方法字面量代替
官方说法:
当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。
说人话:
静态链接:这种方式在编译阶段就已经把符号引用直接转换为了直接引用。
官方说法:
如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。
说人话:
动态链接:这种方式在运行阶段才能把符号引用直接转换为直接引用。
绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。这个不论是编译器确定还是运行期确定都只会发生一次,不会修改。
对应的方法的绑定机制为:早期绑定 (Early Bindng)和晚期绑定(Late Binding)。
官方说法:
早期绑定就是指被调用的目标方法如果在编译期可知,且运行期保持不变时即可将这个方法与所属的类型进行绑定,这样一来,由于明确了被调用的目标方法究竟是哪一个,因此也就可以使用静态链接的方式将符号引用转换为直接引用。
说人话:
早期绑定是和我们的静态绑定相对应的。
官方说法:
如果被调用的方法在编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,这种绑定方式也就被称之为晚期绑定
说人话:
晚期绑定是和我们的动态绑定相对应的。
- class Animal {
- public void eat(){
- System.out.println("动物进食");
- }
- }
-
- interface Huntable{
- void hunt();
- }
-
- class Dog extends Animal implements Huntable{
- @Override
- public void eat(){
- System.out.println("狗吃骨头");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,多管闲事");
- }
- }
-
- class Cat extends Animal implements Huntable{
- @Override
- public void eat(){
- System.out.println("猫吃鱼");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,天经地义");
- }
- }
-
- public class AnimalTest{
- public void showAnimal(Animal animal){
- animal.eat();//晚期绑定
- }
-
- public void showHunt(Huntable h){
- h.hunt();//晚期绑定
- }
-
- }


- class Animal {
- public void eat(){
- System.out.println("动物进食");
- }
- }
-
- interface Huntable{
- void hunt();
- }
-
- class Dog extends Animal implements Huntable{
- @Override
- public void eat(){
- super.eat();//早期绑定
- System.out.println("狗吃骨头");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,多管闲事");
- }
- }
-
- class Cat extends Animal implements Huntable{
- public Cat(){
- super();//早期绑定
- }
- public Cat(String name){
- this();//早期绑定
- }
-
- @Override
- public void eat(){
- System.out.println("猫吃鱼");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,天经地义");
- }
- }
-
- public class AnimalTest{
- public void showAnimal(Animal animal){
- animal.eat();//晚期绑定
- }
-
- public void showHunt(Huntable h){
- h.hunt();//晚期绑定
- }
-
- }
光标放到cat这个类上查看他的jclasslib

invokeSpecial是早期绑定字节码指令,invokevirtual是晚期绑定的字节码指令。
随着高级语言的横空出世,类似于Java一样的基于面向对象的编程语言如今越来越多,尽管这类编程语言在语法风格上存在一定的差别,但是它们彼此之间始终保持着一个共性,那就是都支持封装、继承和多态等面向对象特性
既然这一类的编程语言具备多态特性,那么自然也就具备早期绑定和晚期绑定两种绑定方式。
Java中任何一个普通的方法其实都具备虚函数的特征,也就是运行期才能确定下来,它们相当于c++语言中的虚函数 (c++中则需要使用关键字virtual来显式定义)。
如果在Java程序中不希望某个方法拥有虚函数的特征时,则可以使用关键字final来标记这个方法。也就是一个方法不想被晚期绑定,直接把他给final修饰即可。
文章目录
局部变量表也被称为局部变量数组或者本地变量表
定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用 (reference),以及returnAddress类型。
由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据安全问题
局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的。
参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束。
局部变量表,最基本的存储单元是slot (变量槽)
局部变量表中存放编译期可知的各种基本数据类型(8种),引用类型(reference),returnAddress类型。
在局部变量表里,32位以内的类型只占用一个slot (包括returnAddress类型),64位的类型 (long和double) 占用两个slot。>byte 、short 、char 在存储前被转换为int,boolean 也被转换为int。0 表示false ,非0为true。
long 和double 则占据两个slot。
- import java.util.Date;
-
- public class LocalVariablesTest {
- private int count = 0;
-
- public static void main(String[] args) {
- LocalVariablesTest test = new LocalVariablesTest();
- int num = 10;
- test.test1();
- }
-
- public void test1() {
- Date date = new Date();
- String name1 = "dashu.com";
- String info = test2(date, name1);
- System.out.println(date + name1);
- }
-
- public String test2(Date dateP, String name2) {
- dateP = null;
- name2 = "dashu";
- double weight = 130.5;
- char gender = '男';
- return dateP + name2;
- }
-
- public void test3() {
- count++;
- }
-
- public void test4() {
- int a = 0;
- {
- int b = 0;
- b = a + 1;
- }
- int c = a + 1;
- }
- }
Javap是一个反编译的操作。javap -v 他是一个解析字节码的操作(严格意义上不叫反编译)也可以理解问将字节码文件反编译为汇编的操作,实际上是对字节码文件进行了一个统计解析。方便我们更好的看懂。直接将class文件拖到IDEA当中,可以看到几乎看起来像源码的那么一个东西实际上是IDEA这个开发工具的解析操作。
- PS D:\code\study\hadoop\shit\target\classes> javap -v .\LocalVariablesTest.class
- Classfile /D:/code/study/hadoop/shit/target/classes/LocalVariablesTest.class
- Last modified 2023年11月6日; size 1596 bytes
- SHA-256 checksum 4230277c03e2ed4223bbd78f8d361c86cbf2c473edd66b8c6755f32a7afe64cd
- Compiled from "LocalVariablesTest.java"
- public class LocalVariablesTest
- minor version: 0
- major version: 52
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #3 // LocalVariablesTest
- super_class: #20 // java/lang/Object
- interfaces: 0, fields: 1, methods: 6, attributes: 1
- Constant pool:
- #1 = Methodref #20.#57 // java/lang/Object."
":()V - #2 = Fieldref #3.#58 // LocalVariablesTest.count:I
- #3 = Class #59 // LocalVariablesTest
- #4 = Methodref #3.#57 // LocalVariablesTest."
":()V - #5 = Methodref #3.#60 // LocalVariablesTest.test1:()V
- #6 = Class #61 // java/util/Date
- #7 = Methodref #6.#57 // java/util/Date."
":()V - #8 = String #62 // dashu.com
- #9 = Methodref #3.#63 // LocalVariablesTest.test2:(Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
- #10 = Fieldref #64.#65 // java/lang/System.out:Ljava/io/PrintStream;
- #11 = Class #66 // java/lang/StringBuilder
- #12 = Methodref #11.#57 // java/lang/StringBuilder."
":()V - #13 = Methodref #11.#67 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
- #14 = Methodref #11.#68 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- #15 = Methodref #11.#69 // java/lang/StringBuilder.toString:()Ljava/lang/String;
- #16 = Methodref #70.#71 // java/io/PrintStream.println:(Ljava/lang/String;)V
- #17 = String #72 // dashu
- #18 = Double 130.5d
- #20 = Class #73 // java/lang/Object
- #21 = Utf8 count
- #22 = Utf8 I
- #23 = Utf8
- #24 = Utf8 ()V
- #25 = Utf8 Code
- #26 = Utf8 LineNumberTable
- #27 = Utf8 LocalVariableTable
- #28 = Utf8 this
- #29 = Utf8 LLocalVariablesTest;
- #30 = Utf8 main
- #31 = Utf8 ([Ljava/lang/String;)V
- #32 = Utf8 args
- #33 = Utf8 [Ljava/lang/String;
- #34 = Utf8 test
- #35 = Utf8 num
- #36 = Utf8 test1
- #37 = Utf8 date
- #38 = Utf8 Ljava/util/Date;
- #39 = Utf8 name1
- #40 = Utf8 Ljava/lang/String;
- #41 = Utf8 info
- #42 = Utf8 test2
- #43 = Utf8 (Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
- #44 = Utf8 dateP
- #45 = Utf8 name2
- #46 = Utf8 weight
- #47 = Utf8 D
- #48 = Utf8 gender
- #49 = Utf8 C
- #50 = Utf8 test3
- #51 = Utf8 test4
- #52 = Utf8 b
- #53 = Utf8 a
- #54 = Utf8 c
- #55 = Utf8 SourceFile
- #56 = Utf8 LocalVariablesTest.java
- #57 = NameAndType #23:#24 // "
":()V - #58 = NameAndType #21:#22 // count:I
- #59 = Utf8 LocalVariablesTest
- #60 = NameAndType #36:#24 // test1:()V
- #61 = Utf8 java/util/Date
- #62 = Utf8 dashu.com
- #63 = NameAndType #42:#43 // test2:(Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
- #64 = Class #74 // java/lang/System
- #65 = NameAndType #75:#76 // out:Ljava/io/PrintStream;
- #66 = Utf8 java/lang/StringBuilder
- #67 = NameAndType #77:#78 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
- #68 = NameAndType #77:#79 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- #69 = NameAndType #80:#81 // toString:()Ljava/lang/String;
- #70 = Class #82 // java/io/PrintStream
- #71 = NameAndType #83:#84 // println:(Ljava/lang/String;)V
- #72 = Utf8 dashu
- #73 = Utf8 java/lang/Object
- #74 = Utf8 java/lang/System
- #75 = Utf8 out
- #76 = Utf8 Ljava/io/PrintStream;
- #77 = Utf8 append
- #78 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder;
- #79 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
- #80 = Utf8 toString
- #81 = Utf8 ()Ljava/lang/String;
- #82 = Utf8 java/io/PrintStream
- #83 = Utf8 println
- #84 = Utf8 (Ljava/lang/String;)V
- {
- public LocalVariablesTest();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."
":()V - 4: aload_0
- 5: iconst_0
- 6: putfield #2 // Field count:I
- 9: return
- LineNumberTable:
- line 3: 0
- line 4: 4
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 10 0 this LLocalVariablesTest;
-
- public static void main(java.lang.String[]);
- descriptor: ([Ljava/lang/String;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=3, args_size=1
- 0: new #3 // class LocalVariablesTest
- 3: dup
- 4: invokespecial #4 // Method "
":()V - 7: astore_1
- 8: bipush 10
- 10: istore_2
- 11: aload_1
- 12: invokevirtual #5 // Method test1:()V
- 15: return
- LineNumberTable:
- line 7: 0
- line 8: 8
- line 9: 11
- line 10: 15
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 16 0 args [Ljava/lang/String;
- 8 8 1 test LLocalVariablesTest;
- 11 5 2 num I
-
- public void test1();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=4, args_size=1
- 0: new #6 // class java/util/Date
- 3: dup
- 4: invokespecial #7 // Method java/util/Date."
":()V - 7: astore_1
- 8: ldc #8 // String dashu.com
- 10: astore_2
- 11: aload_0
- 12: aload_1
- 13: aload_2
- 14: invokevirtual #9 // Method test2:(Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
- 17: astore_3
- 18: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
- 21: new #11 // class java/lang/StringBuilder
- 24: dup
- 25: invokespecial #12 // Method java/lang/StringBuilder."
":()V - 28: aload_1
- 29: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
- 32: aload_2
- 33: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- 36: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
- 39: invokevirtual #16 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 42: return
- LineNumberTable:
- line 13: 0
- line 14: 8
- line 15: 11
- line 16: 18
- line 17: 42
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 43 0 this LLocalVariablesTest;
- 8 35 1 date Ljava/util/Date;
- 11 32 2 name1 Ljava/lang/String;
- 18 25 3 info Ljava/lang/String;
-
- public java.lang.String test2(java.util.Date, java.lang.String);
- descriptor: (Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=6, args_size=3
- 0: aconst_null
- 1: astore_1
- 2: ldc #17 // String dashu
- 4: astore_2
- 5: ldc2_w #18 // double 130.5d
- 8: dstore_3
- 9: sipush 30007
- 12: istore 5
- 14: new #11 // class java/lang/StringBuilder
- 17: dup
- 18: invokespecial #12 // Method java/lang/StringBuilder."
":()V - 21: aload_1
- 22: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
- 25: aload_2
- 26: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- 29: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
- 32: areturn
- LineNumberTable:
- line 20: 0
- line 21: 2
- line 22: 5
- line 23: 9
- line 24: 14
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 33 0 this LLocalVariablesTest;
- 0 33 1 dateP Ljava/util/Date;
- 0 33 2 name2 Ljava/lang/String;
- 9 24 3 weight D
- 14 19 5 gender C
-
- public void test3();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- 0: aload_0
- 1: dup
- 2: getfield #2 // Field count:I
- 5: iconst_1
- 6: iadd
- 7: putfield #2 // Field count:I
- 10: return
- LineNumberTable:
- line 28: 0
- line 29: 10
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 11 0 this LLocalVariablesTest;
-
- public void test4();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=3, args_size=1
- 0: iconst_0
- 1: istore_1
- 2: iconst_0
- 3: istore_2
- 4: iload_1
- 5: iconst_1
- 6: iadd
- 7: istore_2
- 8: iload_1
- 9: iconst_1
- 10: iadd
- 11: istore_2
- 12: return
- LineNumberTable:
- line 32: 0
- line 34: 2
- line 35: 4
- line 37: 8
- line 38: 12
- LocalVariableTable:
- Start Length Slot Name Signature
- 4 4 2 b I
- 0 13 0 this LLocalVariablesTest;
- 2 11 1 a I
- 12 1 2 c I
- }
- SourceFile: "LocalVariablesTest.java"
locals=3代表了局部变量表这个数组的长度,这里就是3.
LocalVariableTable后边的内容就是局部变量表: Slot后边的数字是索引数字。name代表了变量的名称。signature标识的是变量类型
args [Ljava/lang/String;代表了参数名为args,L代表的是引用类型。[代表了是数组,整体起来是String类型的数组。
- public static void main(java.lang.String[]);
- descriptor: ([Ljava/lang/String;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=3, args_size=1
- 0: new #3 // class LocalVariablesTest
- 3: dup
- 4: invokespecial #4 // Method "
":()V - 7: astore_1
- 8: bipush 10
- 10: istore_2
- 11: aload_1
- 12: invokevirtual #5 // Method test1:()V
- 15: return
- LineNumberTable:
- line 7: 0
- line 8: 8
- line 9: 11
- line 10: 15
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 16 0 args [Ljava/lang/String;
- 8 8 1 test LLocalVariablesTest;
- 11 5 2 num I
通过Idea种的jclasslib插件继续查看,其本质也是将class文件进行了整理和javap的作用一致。
我们可以看到这个命令是和javap之后的没有任何出入的。



方法嵌套调用的次数由栈的大小决定。一般来说,栈越大,方法嵌套调用次数越多。
对一个函数而言,它的参数和局部变量越多,使得局部变量表膨胀它的栈帧就越大,以满足方法调用所需传递的信息增大的需求。进而函数调用就会占用更多的栈空间,导致其嵌套调用次数就会减少
局部变量表中的变量只在当前方法调用中有效。在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后随着方法栈帧的销毁,局部变量表也会随之销毁