bilibili-JVM学习笔记15 方法执行
The Java Virtual Machine Specification - Java SE 8 Edition
JVM学习笔记11 - Java字节码初识
JVM学习笔记12 - 解读笔记11中的attributes
JVM学习笔记13
JVM学习笔记14 异常
栈帧 stack frame
- 栈帧是一种用于帮助虚拟机执行方法调用与方法执行的
数据结构
;- 方法的局部变量表
- 动态链接信息
- 符号引用
- 直接引用
- 方法的返回地址
- 操作数栈
- 一个栈帧只会归属于某一个线程,所以不存在并发
- 一个线程可能有多个栈帧
- 局部变量表
- solt
- 静态解析
- 有些符号引用是在类加载阶段或是第一次使用时就会转换成为直接引用
-
- 静态方法
-
- 父类方法
-
- 构造方法
-
- 私有方法
- 以上方法称为
非虚方法
,在类加载阶段就可以将符号引用转换为直接引用
- 动态链接
- 另一些符号引用则是在每次运行期都会转换为直接引用,这体现为 java 的多态性
invoke 指令
- invokeinterface : 调用接口中的方法,实际上是在运行期决定的,决定到底调用实现该接口的哪个对象的特点方法;
- invokestatic : 调用静态方法
- invokespecial :
- 调用自己的私有方法
- 调用自己的构造方法
- 调用父类的方法(实例方法或构造方法)
- invokevirtual : 调用虚方法(多态),运行期动态查找的过程
- invokedynamic : jdk1.7 对动态语言的支持
public class InvokeTest1 {
public static void test() {
System.out.println("static");
}
public static void main(String[] args) {
test();
}
}
public new_package.jvm.p51.InvokeTest1();
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 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lnew_package/jvm/p51/InvokeTest1;
public static void test();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String static
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 10: 0
line 11: 8
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: invokestatic #5 // Method test:()V
3: return
LineNumberTable:
line 14: 0
line 15: 3
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 args [Ljava/lang/String;
方法重载
package new_package.jvm.p51;
public class InvokeTest2 {
public void test(Gradepa gradepa) {
System.out.println("gradepa");
}
public void test(Father gradepa) {
System.out.println("Fathet");
}
public void test(Son gradepa) {
System.out.println("Son");
}
public static void main(String[] args) {
Gradepa gradepa1 = new Father();
Gradepa gradepa2 = new Son();
InvokeTest2 main = new InvokeTest2();
main.test(gradepa1);
main.test(gradepa2);
}
}
class Gradepa {
}
class Father extends Gradepa {
}
class Son extends Father {
}
// gradepa
// gradepa
- 方法的静态分派
- Gradepa gradepa1 = new Father();
- gradepa1 的
静态类型
是 Gradepa ,gradepa1 的实际类型
(真正指向的类型)是 Father - 变量的静态类型是不会发生变化的,而变量的实际类型则是可以发生变化的(多态的一种体现),实际类型是在运行期方可确定。
- 方法重载,是一种静态行为
- 方法重写,动态行为
方法重写 (p53)
- 动态分派
- invokevirtual 指令的多态查找流程
-
- 找到
操作数栈
顶的第一个引用的实际类型
- 找到
-
- 寻找
实际类型
是否存在与调用类
匹配的方法描述符
,若存在且访问权限校验通过,则返回实际类型的该方法的直接引用
- 寻找
-
- 若不存在,则按继承顺序从下往上一次查找,查找到则调用
-
- 若一直查找不到,则抛出异常
-
- 方法接收者:方法实际是由哪个对象调用的
- invokevirtual 指令的多态查找流程
package new_package.jvm.p51;
public class StackFrameTest1 {
public static void main(String[] args) {
Animal dog = new Dag();
Animal cat = new Cat();
dog.sleep();
cat.sleep();
dog = new Cat();
dog.sleep();
}
}
class Animal {
public void sleep() {
System.out.println("Animal is sleep");
}
}
class Dag extends Animal {
@Override
public void sleep() {
System.out.println("Dag is sleep");
}
}
class Cat extends Animal {
@Override
public void sleep() {
System.out.println("Cat is sleep");
}
}
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class new_package/jvm/p51/Dag
3: dup
4: invokespecial #3 // Method new_package/jvm/p51/Dag."<init>":()V
7: astore_1
8: new #4 // class new_package/jvm/p51/Cat
11: dup
12: invokespecial #5 // Method new_package/jvm/p51/Cat."<init>":()V
15: astore_2
16: aload_1
17: invokevirtual #6 // Method new_package/jvm/p51/Animal.sleep:()V
20: aload_2
21: invokevirtual #6 // Method new_package/jvm/p51/Animal.sleep:()V
24: new #4 // class new_package/jvm/p51/Cat
27: dup
28: invokespecial #5 // Method new_package/jvm/p51/Cat."<init>":()V
31: astore_1
32: aload_1
33: invokevirtual #6 // Method new_package/jvm/p51/Animal.sleep:()V
36: return
LineNumberTable:
line 7: 0
line 8: 8
line 10: 16
line 11: 20
line 13: 24
line 14: 32
line 15: 36
LocalVariableTable:
Start Length Slot Name Signature
0 37 0 args [Ljava/lang/String;
8 29 1 dog Lnew_package/jvm/p51/Animal;
16 21 2 cat Lnew_package/jvm/p51/Animal;
重载与重写的对比
- 方法重载,是一种静态行为,编译器就可以完全确定;
- 方法重写是动态的,是运行期行为;
virtual method table
- 针对方法调用动态分配的过程,虚拟机会在类的方法区建立一个
虚方法表
的数据结构(virtual method table,vtable
); - 针对 invokeinterface 指令来说,虚拟机会建立一个
接口方法表
的数据结构(interface method table,itable
);