Java中的动态链接VS操作系统动态链接
在操作系统OS中为了优化内存的使用会采用一种动态链接方式,一个文件想要在操作系统中运行必须经过编译、汇编译、链接、装载等步骤。可以参考Java程序是怎么跑起来的。本篇主要讲解Java栈帧中动态链接部分与操作系统的的动态链接的区别与联系
操纵系统为什么需要动态链接(做了解,也可直接略过)
OS是向下统一管理机器硬件、向上给各个应用程序提供统一的系统调用的程序。其中对内存的管理也是重头戏,以下是32位Linux操作系统中虚拟内存的空间分配图
每一个应用程序看到的内存就是这样的一段虚拟内存空间。应用程序的代码指令就存储在.text段也叫代码段,只读。.data段也叫数据段用于存储程序的静态变量、全局变量,可读可写
在应用程序的运行中除了运行自身的代码指令外还需要加载一些系统的公共库,比如用于网络收发的socket库等。在windows中,这些共享库以.dll(Dynamic-Link Libary 动态链接库)结尾,在Linux中以.so(Shared Object)结尾。加载这些共享库可进行对系统资源的调用
静态链接
当应用程序代码经历链接过程生成可执行文件时,每链接一个共享库就将共享库代码复制一份进应用程序的可执行文件中,因此有多少应用程序调用同一个共享库文件,该共享库文件中的代码就在内存中加载多少份
动态链接
在没用动态链接前,系统确实是采用静态链接的方式链接共享库,但是发现对内存的使用是一种极大的浪费,因此动态链接孕育而生。为了达到各个应用程序只加载同一个共享库但内存只存在一份共享库代码,动态链接首先解决的技术问题是地址无关性
PLT、GOT表解决地址无关性
我们都知道程序代码指令加载进内存的代码段是可执行只读的,无法动态的修改代码指令。那么当共享库载入内存中时是怎么被各个不同的应用程序找到的呢?
其实在应用程序的可执行文件加载进内存后,该程序的内存数据段(.data)存在一张GOT(Global Offset Table)全局偏移表,GOT表中,当有需要引用共享库地址的方法指令,都会查询 GOT,根据GOT表找到共享库方法指令的地址位置并调用。因为GOT存在于数据段,因此当共享库发生变化时,应用程序也不需要重新编译,可以直接动态的改变GOT表中的虚拟内存,从而找到最新的共享库。
共享库载入实际的物理内存,虽然物理内存不会变,但是每个应用程序看到的虚拟内存不一样,所以共享库在不同的应用程序中的虚存地址是不一样的,好在每个应用程序都拥有自己的GOT表,能够准确的记录了共享库的位置。这也就达到了地址无关性。
PLT(Procedure Link Table)程序链接表存在于内存的代码段中,主要是用于延迟绑定,我们可以将其理解为跳表。应用程序先是调用PLT表中查询需要调用的GOT表的地址位置,跳到GOT表后查询出共享库的虚存,然后再去调用共享库方法。因为很多动态装载的函数库都是不会被实际调用到的,而共享库中存在非常多的函数,因此采用PLT可达到延迟加载。
像动态链接这样通过修改“地址数据”来进行间接跳转,去调用一开始不能确定位置代码的思路,Java中的多态也采用了这种思想。
Java栈帧中的动态链接
以前的文章中解释了栈帧(拆解栈帧中本地变量表),其中动态链接也是组成栈帧的一部分。在上面对OS的解释中,动态链接是一种技术名称,在Java栈帧这里怎么就成了一个实体了呢?其实根据Java虚拟机对动态链接的描述,翻译成中文就是一个【引用】,那么栈帧存在的这个【引用】是干什么的呢?
在解释这个【引用】的作用之前,还是先说明一点,Java栈帧中的动态链接的目的其实跟OS的是一样的,都是为了节省内存空间,知道这个目的后我们再说明为什么可以节省。也因此在看JVM的时候,我总是会与OS做类比。
这个【引用】在虚机规范的解释为<font color=“#dd0000”>指向运行时常量池的方法引用。每当栈帧中调用其他方法时都会存在一个【引用】。在.class文件中所有的变量和方法引用都是符号引用(Symbolic Reference)也就是下面字节码中的 #数字。比如下面用javap反编译的.class文件中的Constant pool。
public class com.ethan.chapter02.Test02LocalVariablesminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref #7.#28 // java/lang/Object."<init>":()V#2 = Class #29 // com/ethan/chapter02/Test02LocalVariables#3 = Methodref #2.#28 // com/ethan/chapter02/Test02LocalVariables."<init>":()V#4 = Methodref #2.#30 // com/ethan/chapter02/Test02LocalVariables.test3:()V#5 = Long 100l#7 = Class #31 // java/lang/Object#8 = Utf8 <init>#9 = Utf8 ()V#10 = Utf8 Code#11 = Utf8 LineNumberTable#12 = Utf8 LocalVariableTable#13 = Utf8 this#14 = Utf8 Lcom/ethan/chapter02/Test02LocalVariables;#15 = Utf8 main#16 = Utf8 ([Ljava/lang/String;)V#17 = Utf8 args#18 = Utf8 [Ljava/lang/String;#19 = Utf8 variablesTable#20 = Utf8 num#21 = Utf8 I#22 = Utf8 test3#23 = Utf8 q#24 = Utf8 J#25 = Utf8 a#26 = Utf8 SourceFile#27 = Utf8 Test02LocalVariables.java#28 = NameAndType #8:#9 // "<init>":()V#29 = Utf8 com/ethan/chapter02/Test02LocalVariables#30 = NameAndType #22:#9 // test3:()V#31 = Utf8 java/lang/Object
{public com.ethan.chapter02.Test02LocalVariables();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 10: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lcom/ethan/chapter02/Test02LocalVariables;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: new #2 // class com/ethan/chapter02/Test02LocalVariables3: dup4: invokespecial #3 // Method "<init>":()V7: astore_18: bipush 1010: istore_211: aload_112: invokevirtual #4 // Method test3:()V15: returnLineNumberTable:line 12: 0line 13: 8line 14: 11line 15: 15LocalVariableTable:Start Length Slot Name Signature0 16 0 args [Ljava/lang/String;8 8 1 variablesTable Lcom/ethan/chapter02/Test02LocalVariables;11 5 2 num Ipublic void test3();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=4, args_size=10: ldc2_w #5 // long 100l3: lstore_14: bipush 106: istore_37: returnLineNumberTable:line 20: 0line 21: 4line 22: 7LocalVariableTable:Start Length Slot Name Signature0 8 0 this Lcom/ethan/chapter02/Test02LocalVariables;4 4 1 q J7 1 3 a I
}
在.class文件中的常量池会随着文件被加载而转换进JVM中的运行时常量池中。由于存在了这些【符号引用】,可以使用Java层面的动态链接技术,将这些符号引用转换为调用方法的直接引用。比如字节码中的 invokevirtual指令就能够支持动态链接。
在类加载子系统中,一个.class文件被加载进JVM共需要经历3步骤,加载-链接-初始化。而在链接阶段中的第三步【解析】的目的就是将常量池内的符号引用转换为直接引用的过程,也就是动态链接产生的过程。
我们类比一下OS的动态链接与Java的动态链接。Java的.class文件类比于OS的每一个应用程序的可执行文件,.class文件中的常量池类比于GOT表,java的运行时常量池类比于共享库。java产生动态的链接是在.class的解析阶段,根据.class文件中的符号引用去查询常量池然后,将.class文件中的符号引用转换为直接应用,并存于栈帧中。
因为在加载不同的.class文件时,都可能调用相同的常量或者方法,所以只需要在运行时常量池存储一份,然后记录其直接引用即可,因此节省了空间。
解释完Java层面的动态链接我们也就能解释Java多态的实现过程了,在Java源代码编译期间方法的重写导致无法确认出调用方法的真正位置,只有在运行时将符号引用转为为直接应用确定方法的位置。这个过程也就是在【解析】阶段实现的。
这种编译时期无法确定方法的调用位置,只能够在程序运行期根据实际的类型绑定相关方法,这种绑定方式也就被称之为晚期绑定。
相关文章:

Java中的动态链接VS操作系统动态链接
在操作系统OS中为了优化内存的使用会采用一种动态链接方式,一个文件想要在操作系统中运行必须经过编译、汇编译、链接、装载等步骤。可以参考Java程序是怎么跑起来的。本篇主要讲解Java栈帧中动态链接部分与操作系统的的动态链接的区别与联系 操纵系统为什么需要动态…...

深入理解Linux虚拟内存管理(七)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理(一) 深入理解Linux虚拟内存管理(二) 深入理解Linux虚拟内存管理(三) 深入理…...
GSR II 智能速度辅助系统的型式认证和系统作为独立技术单元的型式认证测试流程和技术要求
智能速度辅助系统ISA的型式认证和系统作为独立技术单元的型式认证测试流程和技术要求 补充欧洲议会和欧洲理事会第2019/2144号条例,为机动车智能速度辅助系统的型式认证和这些系统作为独立技术单元的型式认证规定了详细的测试程序和技术要求,并修订该条例的附件二 (1)(EU…...

工厂方法模式(五)
过气的,终究是过气了 上一章简单介绍了工厂模式(四), 如果没有看过,请观看上一章 一.工厂方法模式 工厂方法模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。 将类的实例化(具体产品的创建&…...

力扣笔记(每日随机一题)——最佳买卖股票时机含冷冻期
问题(中等) 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 卖出股票后&a…...

yolov5 6.1 关于 tensorrt 加速的使用以及问题说明
文章目录 1. 参考连接2. 使用说明2.1 导出加速模型2.1 使用加速模型2.2 加速参数对比 3. 问题说明3.1 在 Tensorrt 8.4.1.5 版本上使用 export.py 导出失败的问题3.2 把模型文件由 best.pt 更换成加速后的 best.engine 后,执行推理时标注的类别名不正确的问题3.3 导…...
SVR(支持向量机)用法介绍
一、SVR回归介绍 SVR(Support Vector Regression)是支持向量机(SVM)在回归问题中的应用。与SVM分类模型相似,SVR也是一种非概率性算法,通过使用核函数将数据映射到高维空间,并在该空间上寻找最优的超平面与训练数据之间的间隔最大化…...

是面试官放水,还是公司实在是太缺人?这都没挂,腾讯原来这么容易进···
本人211非科班,之前在字节和腾讯实习过,这次其实没抱着什么特别大的希望投递,没想到腾讯可以再给我一次机会,还是挺开心的。 本来以为有个机会就不错啦!没想到能成功上岸,在这里要特别感谢帮我内推的同学&…...

算法模板(5):数学(1):数学知识(1)
数论 整数的整除性 [x]表示不超过x的最大整数,叫做取整函数或高斯函数。设整数a,b不同时为零,则存在一对整数m,n,使得 ( a , b ) a m b n (a, b) am bn (a,b)ambn。注:a和b的最大公因数会写成 (a, b)…...

电子行业 K 公司对接 Nexperia EDI 项目案例
项目背景 Nexperia 是一家全球领先的半导体制造商,专注于提供高性能、高可靠性和创新性的半导体解决方案。公司成立于2017年,是前飞思卡尔半导体业务的一部分,并在全球范围内拥有多个设计、研发和生产基地。 Nexperia 使用 EDI(…...

chatgpt赋能python:Python如何将英文转化为中文的最佳方法
Python如何将英文转化为中文的最佳方法 介绍 在现代全球化社会中,国与国之间的交流越来越频繁,相应的语言翻译工具的需求也愈发迫切。Python是一种易于学习、快速上手的编程语言,适合初学者和经验丰富的程序员使用,在语言翻译方…...

知道这些英文文档翻译的方式吗
在工作中,大家有没有遇到领导交给你一份外语的文档,要你去观看和理解,但是我们看不太懂或者没啥时间去一点点翻译怎么办呢?我们就需要有工具来将文档翻译,它是一项非常实用和便捷的功能,它可以将文档中的文…...

供应链安全
供应链安全 目录 文章目录 供应链安全目录本节实战可信任软件供应链概述构建镜像Dockerfile文件优化镜像漏洞扫描工具:Trivy检查YAML文件安全配置:kubesec准入控制器: Admission Webhook准入控制器: ImagePolicyWebhook关于我最后…...

华硕天选4原装Windows11系统带ASUSRECOVERY恢复工厂模式安装
华硕工厂恢复系统 ,安装结束后带隐藏分区以及机器所有驱动软件,奥创Myasus Recovery 文件地址https://pan.baidu.com/s/1Pq09oDzmFI6hXVdf8Vqjqw?pwd3fs8 提取码:3fs8 文件格式:5个底包(HDI KIT COM MCAFEE EDN) 1个引导工具TLK 支持ASUSRECOVERY型…...

数据库期末复习(8)并发控制
笔记 数据库DBMS并发控制(1)_旅僧的博客-CSDN博客 数据库 并发控制(2)死锁和意向锁_旅僧的博客-CSDN博客 同一个对象不能既有slock又有xlock; 冲突可串行化和锁 怎么判断是否可以进行冲突可串行化:简便的方法是优先图 只有不同对象和同一对象都是读才不能发生非串行化调…...

一文说透:低代码开发平台和零代码平台区别是什么?
低代码开发平台和零代码平台区别是什么? 一个简单的例子就可以解释清楚。 假设你想入住一套新房,回看住房变迁史: 最原始方式是:自己建造往后一点,交付“毛坯房”:开发商统一建小区,不需要自…...

4.将图神经网络应用于大规模图数据(Cluster-GCN)
到目前为止,我们已经为节点分类任务单独以全批方式训练了图神经网络。特别是,这意味着每个节点的隐藏表示都是并行计算的,并且可以在下一层中重复使用。 然而,一旦我们想在更大的图上操作,由于内存消耗爆炸,…...
pymongo更新数据
使用 PyMongo,可以通过以下步骤将查询到的记录进行更新: 下面是一个简单的示例代码片段,展示如何向名为users的集合中的所有文档添加一个新字段age。 import pymongo # 连接 MongoDB client pymongo.MongoClient("mongodb://localh…...
手机软件测试规范(含具体用例)
菜单基本功能测试规范一、短消息功能测试规范测试选项操作方法观察与判断结果创建、编辑短消息并发送书写短消息1、分别使用菜单或快捷方式进入书写短消息是否有异常; 2、输入0个字符,选择、输入号码发送,应成功; 3、输入1个中文…...
mysql having的用法
having的用法 having字句可以让我们筛选成组后的各种数据,where字句在聚合前先筛选记录,也就是说作用在group by和having字句前。而 having子句在聚合后对组记录进行筛选。我的理解就是真实表中没有此数据,这些数据是通过一些函数生存。 SQ…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...