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

news2024/9/21 2:35:24
动态链接–指向运行时常量池的方法引用
  • 每一个栈帧内部包含一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的为了支持当前方法的代码能够实现动态链接(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寄存器值等,让调用者方法继续执行下去

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【字典树(前缀树) 位运算】1803. 统计异或值在范围内的数对有多少

本文涉及知识点 字典树(前缀树) 位运算 LeetCode1803. 统计异或值在范围内的数对有多少 给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:low 和 high ,请返回 漂亮数对 的数目。 漂亮数对 是…

运维出现的问题 --集成

运维出现的问题 集成 macos 本地打的镜像,推到线上出现 images platform (linux/arm64) does not match the detected解决办法 macos 本地打的镜像,推到线上出现 image’s platform (linux/arm64) does not match the detected WARNING: The requested …

抖音视频怎么去水印保存部分源码|短视频爬虫提取收集下载工具

抖音视频怎么去水印保存部分源码|短视频爬虫提取收集下载工具 抖音视频去水印保存部分源码: 通过使用Python中的requests、re和os等库,可以编写如下代码来实现抖音视频去水印保存的功能。 短视频爬虫提取手机下载工具的使用方法: 该工具主…

项目集成SkyWalking,基于k8s搭建

一、搭建SkyWalking 官方文档(英文):skywalking/docs at master apache/skywalking 中文可以使用:GitHub - SkyAPM/document-cn-translation-of-skywalking: [已过期,请使用官网AI文档] The CN translation version of Apache…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-19讲 串口实验UART

前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

跟着Kimi学习结构化提示词:19套内置提示词都在这里了!

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,所以创建了“AI信息Gap”这个公众号,专注于分享AI全维度知识…

Android Studio 版本升级后 Gradle project sync failed(Android V 应用升级)

问题及解决方案 更新到蜥蜴 Android Studio Iguana 后,出现Gradle project sync failed的问题(IDE更新版本的常态了)。 背景:对应用进行Android V版本升级(SDK35,gradle插件版本要 8.4.0) 1、…

cn.hutool.poi.excel 实现excel导出效果 首行高度,行样式,颜色,合并单元格,例子样式

需求 接了需求,下载excel模版,本来看着还是简单的,然后实现起来一把泪,首先是使用poi,我查了好久,才实现,然后是我用easyexcel又实现了一遍,用了一个周多才实现。 这是需求&#x…

Linux|ubuntu22.04安装CUDA最新完整教程

文章目录 一、安装前准备工作查看GPU和型号查看GCC版本*下载gcc12 *检查驱动 二、安装CUDA Toolkit*安装驱动 三、安装后的工作必要操作推荐的操作开启守护进程模式删除本地下载安装包 四、验证删除CUDA常见问题及解决方案还需要安装cuDNN吗?nvcc: No such file or …

GitKraken克隆Git仓库

克隆Git仓库 修改本地仓库 在此新增了一个test.txt文件 GitKraken提醒有一处改变 暂存(Stage)该文件,添加描述后提交修改: 修改成功:

【数据结构】【C语言】堆~动画超详细解读!

目录 1 什么是堆1.1 堆的逻辑结构和物理结构1.2 堆的访问1.3 堆为什么物理结构上要用数组?1.4 堆数据上的特点 2 堆的实现2.1 堆类型定义2.2 需要实现的接口2.3 初始化堆2.4 销毁堆2.5 堆判空2.6 交换函数2.7 向上调整(小堆)2.8 向下调整(小堆)2.9 堆插入2.10 堆删除2.11 //堆…

Flink常见面试题总结

文章目录 1. 简单介绍一下Flink2. Flink 的运行必须依赖Hadoop组件吗?3. Flink 和 Spark Streaming 的区别?4. Flink集群角色5. Flink核心概念5.1 并行度5.2 算子链(Operator Chain)5.3 任务槽(Task Slots)5.4 任务槽…

Python | Leetcode Python题解之第108题将有序数组转换为二叉搜索树

题目: 题解: class Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:def helper(left, right):if left > right:return None# 选择任意一个中间位置数字作为根节点mid (left right randint(0, 1)) // 2root TreeNode(nums…

cesium绘制三角网可视化及mesh网格数据解析

可视化运行效果(水质污染扩散) 实现运行效果 术语 Mesh网格数据解析 Mesh(网格)在不同领域有不同的应用和定义。在计算机网络中,Mesh网络指的是一种无中心的网状结构,每个节点都与其他节点相连。而在3D计算机图形学中&#…

计算机专业实习生应该去哪实习?

计算机专业实习生可以选择在各种不同类型的公司和组织中实习。我这里有一套编程入门教程,不仅包含了详细的视频讲解,项目实战。如果你渴望学习编程,不妨点个关注,给个评论222,私信22,我在后台发给你。 这取…

局部直方图均衡化去雾算法

目录 1. 引言 2. 算法流程 3. 代码 4. 去雾效果 1. 引言 局部直方图算法是一种基于块的图像去雾方法,它将图像分割为若干个块,并在每个块内计算块的局部直方图。通过对各个块的直方图进行分析和处理,该算法能够更好地适应图像中不同区域的…

聚鼎科技:现在的装饰画怎么样

当代装饰画以其多样化的风格和形式,在装点生活的同时反映了现代人的审美趣味和文化追求。如今,装饰画不再局限于传统的油画、水彩或版画,它们已经跨越了材质与技法的界限,呈现出前所未有的丰富性。 走进任一家装潢精致的咖啡馆或现…

基于机器学习模型预测信用卡潜在用户(XGBoost、LightGBM和Random Forest)

基于机器学习模型预测信用卡潜在用户(XGBoost、LightGBM和Random Forest) 随着数据科学和机器学习的发展,越来越多的企业开始利用这些技术来提高运营效率。在这篇博客中,我将分享如何利用机器学习模型来预测信用卡的潜在客户。此…

angr使用学习

首先我是直接在kali中安装的,也是边练边学的。 嗯,要在纯净python环境,所以是在 virtualenv 虚拟环境里,也不是特别会用这个,按照教程一步步做的 source venv/bin/activate 进入了对应环境 退出是 deactivate en,ipy…

pdf文件怎么编辑?分享3个专业的pdf软件!

在数字化时代,PDF文件已成为我们工作、学习中的得力助手。然而,面对需要修改的PDF文件,许多人却感到无从下手。今天,就让我们一起探索如何轻松编辑PDF文件,并介绍几款实用的编辑软件,让你轻松应对各种PDF编…