前言
面向的读者:
- 有一定java基础
- 想系统学习JVM基础知识
文章目的:
- 自己读书笔记
- 精炼知识点,实现输入输出闭环
一、运行时栈帧结构
JVM 运行的基本单元就是栈帧(Stack Frame)。

在活动线程中,只有位于栈顶的方法才是运行的。一个stack frame 结构如下:
- 局部变量表(local variable table): 方法参数和方法内部定义的局部变量。按照slot 的方式进行分配。一个slot 是32位。double 和long 类型占用两个slot。
- 操作数栈(operand stack):FIFO,每次都操作最顶端的数据。
- 动态连接(dynamic link):常量池中的符号,通过动态连接转换成真正的内存地址。这种转换也分两种。静态解析:在加载时就已经确定地址。动态解析:在运行时才决定最终的物理地址。
- 方法返回地址
二、方法调用
1. 解析
编译时可知,运行时不可变: 在加载时就已经知道方法调用的具体地址了。
- 静态方法:与类型直接关联
- 私有方法:在外部不可用
2. 分派
2.1 静态分派
package com.manulife;
public class StaticDispatch {
static abstract class Human{}
static class Man extends Human{}
static class Woman extends Human{}
public void sayHello(Human guy){
System.out.println("hello, guy");
}
public void sayHello(Man guy){
System.out.println("hello, gentleman");
}
public void sayHello(Woman guy){
System.out.println("hello, lady");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
}
上面的代码,两次调用的结果都是 hello, guy. 为什么呢?
静态类型:Human 。 实际类型/运行时类型 : Man/Woman. 静态类型和动态类型在程序中都可能发生变化,区别是
动态类型:在使用时发生
运行时类型:在运行期间才可知。
//实际类型在运行时才知道
Human human = (new Random()).nextBoolean() ? new Man() : new Woman();
//静态类型变化
sr.sayHello((Man)human);
sr.sayHello((Woman)human );
2.2 动态分派
public class DynamicDispatch {
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello() {
System.out.println("Man say hello");
}
}
static class Woman extends Human{
@Override
protected void sayHello() {
System.out.println("woman say hello");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello(); //invokevirtrual #22; //Method DynamicDispatch$Human.sayHello:()V
woman.sayHello();//invokevirtrual #22; //Method DynamicDispatch$Human.sayHello:()V
man = new Woman();
man.sayHello();//invokevirtrual #22; //Method DynamicDispatch$Human.sayHello:()V
}
}
从反编译的结果来看,在call 方法sayHello 的时候,生成的命令都是一样的。为什么会在运行时调用不同的方法,这个就得从 invokevirtual 命令说起:
invokevirtual 命令的执行过程如下:

另外: 由于invokevirtual指针对方法,因此字段不存在多态,当子类中含有与父类一样的字段时,会覆盖父类的字段。
本文主要介绍了JVM中的运行时栈帧结构,包括局部变量表、操作数栈、动态连接和方法返回地址。同时,详细探讨了方法调用的解析和分派机制,通过实例解释了静态分派和动态分派的区别,并分析了invokevirtual指令在动态分派中的作用。
- JVM字节码执行引擎&spm=1001.2101.3001.5002&articleId=116229119&d=1&t=3&u=76f688a617b340378213f5910a085710)
465

被折叠的 条评论
为什么被折叠?



