《深入理解java虚拟机》读书笔记(5)- JVM字节码执行引擎

本文主要介绍了JVM中的运行时栈帧结构,包括局部变量表、操作数栈、动态连接和方法返回地址。同时,详细探讨了方法调用的解析和分派机制,通过实例解释了静态分派和动态分派的区别,并分析了invokevirtual指令在动态分派中的作用。

前言

面向的读者:

  • 有一定java基础
  • 想系统学习JVM基础知识

文章目的:

  • 自己读书笔记
  • 精炼知识点,实现输入输出闭环

一、运行时栈帧结构

JVM 运行的基本单元就是栈帧(Stack Frame)。
runtime stack
在活动线程中,只有位于栈顶的方法才是运行的。一个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 命令的执行过程如下:

invoke
另外: 由于invokevirtual指针对方法,因此字段不存在多态,当子类中含有与父类一样的字段时,会覆盖父类的字段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值