JVM学习-动态链接和方法返回地址

动态链接–指向运行时常量池的方法引用
  • 每一个栈帧内部包含一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的为了支持当前方法的代码能够实现动态链接(Dynamic Linking),如invokednamic指令。
  • 在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池中,比如一个方法调用了另一个方法,就是通过常量池中方法的符号引用来表示的,动态链接的作用是为了将这些符号引用转换为调用方法的直接引用。
    在这里插入图片描述
为什么需要常量池?

常量池的作用,是为了提供一些符号和常量,便于指令的识别

方法的调用

在JVM中,将符号引用转换为调用方法的直接引用与方法的绑定机制相关,绑定机制分为早期绑定和晚期绑定,绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。

  • 早期绑定:指被调用的目标方法如果在编译期可知,且运行期保持不变时,即可将这个方法与所属的类型进行绑定,这样一来,由于明确了被调用的目标方法究竟是哪一个,因此也可以使用静态链接的方式将符号引用转换为直接引用
  • 晚期绑定:如果被调用的方法在编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,称为晚期绑定
静态链接

当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知,且运行期间保持不变,这种情况下将调用方法的符号引用转换为直接引用的过程称为静态链接

动态链接

如果被调用方法在编译期无法被确定下来,只能够在程序运行期调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此被称为动态链接

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 {
    public Cat() {
        super();   //早期绑定
    }
    public Cat(String name) {
        this();    //早期绑定
    }
    @Override
    public void eat() {
    	super.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(); //晚期绑定
    }
}

虚方法和非虚方法
  • 非虚方法:方法在编译期就确定了具体的调用版本,这个版本在运行时是不可变的,这种方法称为非虚方法。

  • 静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。

  • 其它方法为虚方法

  • 普通调用指令

    • invokestatic:调用静态方法,解析阶段确定唯一方法版本
    • invokespecial:调用方法,私有及父类方法,解析阶段确定唯一方法版本
    • invokevirtual:调用所有虚方法
    • invokeinterface:调用接口方法
  • 动态调用指令

    • invokedynamic:动态解析出需要调用的方法,然后执行
      普通调用指令固化在虚拟机内部,方法的调用执行不可人为干预,而invokedynamic指令则支持由用户确定方法版本,其中invokestatic指令和invokespecial指令调用的方法称为非虚方法,其余的(final修饰的除外)称为虚方法
      静态类型语言是判断变量自身的类型信息,动态类型语言是判断变量值的类型信息,变量没有类型信息,变量值才有类型信息。
class Father {
    public Father() {
        System.out.println("father构造器");
    }
    public static void showStatic(String str) {
        System.out.println("father " + str);
    }
    
    public final void showFinal() {
        System.out.println("father show final");
    }
    public void showCommon() {
        System.out.println("father 普通方法");
    }
}
public class Son extends Father{
    public Son() {
        super();
    }
    public Son(int age) {
        this();
    }
    //非重写父类的方法,静态方法不允许重写
    public static void showStatus(String str) {
        System.out.println("son " + str);
    }
    private void showPrivate(String str) {
        System.out.println("son private " + str);
    }
    public void show() {
        //invokestatic
        showStatic("lotus.com");
        //invokestatic
        super.showStatic("goods!");
        //invokespecial
        showPrivate("hellow!");
        //invokespecial
        super.showCommon();
        //invokevirtual,此方法声明有final,不能被子类重写,此方法为非虚方法
        showFinal();
        //invokevirtual
        showCommon();
        info();
        MethodInterface in = null;
        //invokeinterface
        in.methodA();
    }

    private void info() {
    }
    public void display(Father f) {
        f.showCommon();
    }

    public static void main(String[] args) {
        Son so = new Son();
        so.show();
    }
}
interface MethodInterface {
    void methodA();
}
@FunctionalInterface
interface Func {
    public boolean func(String str);
}
public class Lambda {
    public void lambda(Func func) {
        return;
    }

    public static void main(String[] args) {
        Lambda lambda = new Lambda();
        //invokedynamic
        Func func = s-> {
          return true;
        };
        //invokedynamic
        lambda.lambda(s-> {
            return true;
        });
    }
}
方法重写本质
  1. 找到操作数栈顶的第一个元素所执行的对象的实际类型,记作C
  2. 如果过程结束,不通过类型C中找到与常量中的描述符符合简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找不到,则返回java.lang.IllegalAccessError异常
  3. 否则,按继承关系从下向上依次对C的各个父类进行第2步搜索和验证过程
  4. 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。
虚方法表
  • 在面向对象的编程中,会频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话,就可能影响到执行效率,因此,为了提高性能,JVM采用在类的方法区建立一个虚方法表(非虚方法不会出现在表中)来实现,使用索引表代替查找
  • 每个类中都有一个虚方法表,表中存放各个方法的实际入口
  • 虚方法表会在类加载的链接阶段被创建并开始初使化,类的变量初使值准备完成之后,JVM会把该类的方法一也初始化完毕
方法返回地址
  • 存放调用该方法的PC寄存器的值
  • 一个方法结束,有两种方式:
    • 正常执行完成
      • 一个方法在正常调用完成之后究竟需要使用哪一个返回指令还需要根据方法返回值的实际数据类型而定
      • 在字节码指令中,返回指令包含ireturn(当返回值是boolean,byte,char,short,int类型时),lreturn,freturn,dreturn以及areturn,另外还有一个return指令供声明为void方法,实例化初始化方法、类和接口的初始化方法使用。
    • 出现未处理的异常,非正常退出
      • 方法执行过程中抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码。
  • 无论哪种方式退出,在方法退出后都返回到该方法被调用的位置,方法正常退出时,调用者PC寄存器的值做为返回地址,好调用该方法的指令的下一条指令地址,而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;

/**
 * Administrator
 * 2024/5/17
 */
public class ReturnAddressTest {
    //ireturn
    public boolean methodBoolean() {
        return false;
    }
    //ireturn
    public byte methodByte(){
        return 0;
    }
    //ireturn
    public short methodShort(){
        return 0;
    }
    //ireturn
    public char methodChar(){
        return 'a';
    }
    //ireturn
    public int methodInt(){
        return 0;
    }
    //lreturn
    public long methodLong(){
        return 0L;
    }
    //freturn
    public float methodFloat(){
        return 0.0f;
    }
    //dreturn
    public double methodDouble(){
        return 0.0;
    }
    //areturn
    public String methodString() {
        return null;
    }
    //areturn
    public Date methodDate(){
        return null;
    }
    //return
    public void methodVoid(){

    }
    static {
        int i = 10;
    }
    public void method2() {
        methodVoid();
        try {
            method1();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void method1() throws IOException {
        FileReader fr = new FileReader("lotus.txt");
        char[] cBuffers = new char[1024];
        int len;
        while ((len = fr.read(cBuffers))!=-1) {
            String str = new String(cBuffers,0,len);
            System.out.println(str);
        }
        fr.close();
    }

    public static void main(String[] args) {

    }
}

方法返回地址本质上,方法的退出就是当前栈帧出栈的过程,此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去

正常完成出口和异常完成出口区别:通过异常完成出口退出的不会给他的上层调用者产生任何返回值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/636044.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

异步爬虫学习实战项目:水效标识网

大家好,我是南枫,今天一起来学习异步爬虫。 文章开始之前,我们先搞清楚为什么要学异步爬虫?我们之后在工作中会遇到爬大量数据,比如百万数据采集,用平常的方法爬取的效率会比较低,所以要学习异…

AI应用案例:电能量异常分析智能诊断系统

窃电和计量装置故障造成漏收、少收电费使电力系统利益受损。一般情况主要通过定期巡检、定期校验电表、用户举报窃电等手段来发现窃电或计量装置故障。对人的依赖性太强,抓窃查漏的目标不明确。利用电力系统中逐步积累下来的海量真实数据,采用数据挖掘技…

CSDN智能总结助手

github项目地址: https://github.com/anjude/little-demo/tree/master 获取CSDN的user name和user token 打开csdn,打开控制台 - Application - Cookies,找到domain为blog.csdn.net的cookie,复制user_name和user_token的值 把上…

业务逻辑漏洞安全指南

业务逻辑漏洞安全指南 在当今互联的数字环境中,保护业务逻辑流程的安全对于维护企业应用程序的完整性、机密性和可用性至关重要。业务逻辑漏洞可能被利用来执行未经授权的操作、破坏服务或窃取敏感数据。 1. 身份验证、会话管理和访问控制 所有高价值的业务逻辑流…

labview_开放协议

一、开放协议 二、硬件设置 英格索兰硬件设置: 三、配套测试软件 四、Labview代码

【优选算法】模拟 {经验总结;相关编程题解析}

一、经验总结 模拟题型的算法原理相对简单,就是依葫芦画瓢:题目中怎样描述,算法就怎样执行。考验的主要是将实际问题转换为代码的能力。 但是模拟题型并不是只能傻乎乎的按步骤编码,也可以先将模拟算法的流程通过举例或绘图演示…

gpt-4o考场安排

说明 :经过多次交互,前后花了几个小时,总算完成了基本功能。如果做到按不同层次分配考场,一键出打印结果就完美了。如果不想看中间“艰苦”的过程,请直接跳到“最后结果”及“食用方法”。中间过程还省略了一部分交互&…

支付风险智能风控应用与评估指引

伴随宏观经济环境变化、支付监管愈趋从严、金融科技不断创新、支付参与主体日趋多元,支付行业正面临着业务发展与合规经营、支付便捷与安全、数据挖掘与隐私保护等诸多挑战,支付风险的复杂性与日俱增,共同建设安全支付生态的必要性不断凸显&a…

探索 Rust 语言的精髓:深入 Rust 标准库

探索 Rust 语言的精髓:深入 Rust 标准库 Rust,这门现代编程语言以其内存安全、并发性和性能优势而闻名。它不仅在系统编程领域展现出强大的能力,也越来越多地被应用于WebAssembly、嵌入式系统、分布式服务等众多领域。Rust 的成功&#xff0…

【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法

文章目录 前言 Timer中断调度 Event中断调度 StateFlow调度 分析和应用 总结 参考资料 前言 近期在一些嵌入式系统开发项目中,在使用嵌入式处理器时,遇到了挺多费时费力的事情。所以利用晚上和周末时间,在这些方面深入研究了一下&…

等保测评-安全通信网络与安全区域边界

等保测评,全称为网络安全等级保护测评,是中国网络安全领域的一项重要工作,旨在通过标准化的测评流程,确保信息系统的安全等级保护措施符合国家相关标准。在等保测评中,"安全通信网络"与"安全区域边界&q…

【机器学习与大模型】开源大模型和闭源大模型:技术发展与社会责任的平衡点

目录 💡引言✈️✈️一,开源大模型的优势与劣势✈️✈️1.1 优势:✈️✈️1.2 挑战和劣势: 🚀🚀2. 闭源大模型的优势与劣势🚀🚀2.1 优势:🚀🚀2.2 …

App推广排名:ASO三大优化策略

ASO优化帮助产品在应用市场上获得更高的排名。而且对于APP产品来说,ASO在合理控制成本的要求下,能带来多方面看得见的提升。小柚在过去的十年里,和教育、金融、医疗、工业等多个领域的老板达成合作,并取得了优秀的成绩。 一、提升…

优雅草便民工具v2.0.4更新

优雅草便民工具v2.0.4更新 优雅草便民工具v2.0.4更新 2024年5月20日v2.0.4更新优雅草便民工具youyacao-tools-增加淘宝联想词功能和ai绘画功能apk下载 https://fenfacun.youyacao.com/tools204.apk 介绍 优雅草便民工具是一款由成都市一颗优雅草科技有限公司打造的便民查询公益…

web4.0-元宇宙虚拟现实

互联网一直在不断演变和改变我们的生活方式,从Web逐渐 1.0时代的静态网页到Web 2.0时代的社会性和内容制作,再从Web逐渐 在3.0阶段,互联网发展一直推动着大家时代的发展。如今,大家正站在互联网演化的新起点上,迈入Web…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-38-如何截图-下篇

宏哥微信粉丝群:https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 这个系列的文章也讲解和分享了差不多三分之一吧,突然有小伙伴或者童鞋们问道playwright有没有截图的方法。答案当然是:肯定有的。宏哥回过头来看看确实…

SAPUI5基础知识2 - 手动创建一个SAPUI5的项目

1. 前言 在本篇文章中,我们将手动一步一步建立出第一个SAPUI5的 ‘Hello World!’ 项目。 2. 步骤详解 2.1 在BAS中建立Dev Space 进入SAP Business Application Studio的Dev Space Manger,选择创建Dev Space。 勾选HTML5 Application Template插件…

【算法】二分算法——山脉数组的峰顶索引

该题用二分算法解“山脉数组的峰顶索引”,有需要借鉴即可。 目录 1.题目2.总结 1.题目 题目链接:LINK 暴力求解很简单,这里不再提及。 这个可以根据峰顶值分为两部分,因而具有“二段性”,可以用二分算法&#xff0c…

一个超级简单的Python UI库:NiceGUI

大家好,图形用户界面(GUI)的开发往往被看作是一项复杂且繁琐的任务。Python作为一门强大的编程语言,提供了许多优秀的库来帮助开发者实现这一目标。今天,我们将介绍一个名为NiceGUI的库,它能帮助你轻松构建…