Java中的动态链接VS操作系统动态链接

news/2024/11/2 6:32:03/

在操作系统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源代码编译期间方法的重写导致无法确认出调用方法的真正位置,只有在运行时将符号引用转为为直接应用确定方法的位置。这个过程也就是在【解析】阶段实现的

这种编译时期无法确定方法的调用位置,只能够在程序运行期根据实际的类型绑定相关方法,这种绑定方式也就被称之为晚期绑定。


http://www.ppmy.cn/news/361248.html

相关文章

防雷抗浪涌插排插座推荐,同为科技(TOWE)防雷桌面PDU安全可靠

同为科技TOWE双排防雷抗浪涌桌面PDU插座 随着夏天天气越来越热&#xff0c;强对流天气增多&#xff0c;雷雨天气频发。在雷电季节&#xff0c;通常影响家用电器安全的主要原因是由于雷电感应的侵入&#xff0c;特别是对绝缘强度低、过电压耐受力差的微电子产品影响甚大。而所谓…

遍历JSON将字符放入数组中

处理JSON&#xff1a;function traverseJSON(obj, arr) {for (var key in obj) {if (typeof obj[key] string) {arr.push(obj[key]);} else if (typeof obj[key] object) {traverseJSON(obj[key], arr);}} }traverseJSON({}, [])遍历数组查找下标&#xff1a; function compa…

大数据技术Flink详解

一、有状态的流式处理 Apache Flink 是一个分布式流处理器,具有直观和富有表现力的API,可实现有状态的流处理应用程序。它以容错的方式有效地大规模运行这些应用程序。Flink 于2014 年4 月加入Apache 软件基金会作为孵化项目,并于2015 年1 月成为顶级项目。从一开始,Flink …

Google 程序员都是怎么研究 AI 的?

作者 | 赵敏 责编 | 郭芮 9 月 20 日-10 月 7 日&#xff0c;谷歌AI体验展在上海龙美术馆&#xff08;西岸馆&#xff09;展出。谷歌总共展出了 17 个互动项目&#xff0c;和九月底人工智能大会上谷歌介绍的项目有一些重叠&#xff0c;也有一些区别。这次体验展没有背后的技术原…

思科九年(转载自Internet)

第1节&#xff1a;思科九年(1) 序 南半球的二月是盛夏。这里白天的阳光炽烈而持久&#xff0c;四处都是耀眼的惨白。电视里的广告说皮肤癌是这个国家的国癌&#xff0c;提醒人们小心这厉害的阳光&#xff1a;要穿长袖的衣服待在阴凉的地方&#xff0c;要戴墨镜涂防晒霜。即便如…

urule规则引擎学习笔记

规则引擎是什么 规则引擎是一种嵌入在应用程序中的组件&#xff0c;它可以将业务规则从业务代码中剥离出来&#xff0c;使用预先定义好的语义规范来实现这些剥离出来的业务规则&#xff1b;规则引擎通过接受输入的数据&#xff0c;进行业务规则的评估&#xff0c;并做出业务决…

那四年,我们一起逝去的青春

今天是2011年10月1日&#xff0c;是我出生后的第21个国庆节&#xff0c;也是大学生涯里最后一个国庆节&#xff0c;这篇日志可能有点长&#xff0c;闲着蛋疼的童鞋可以泡杯咖啡&#xff0c;一边喝一边看&#xff0c;就当看笑话好了。日志发出来估计已经是几个月后的事了&#x…

人机交互

一.Material Design简介 Material Design是由谷歌的设计团队创建的一种设计语言&#xff0c;旨在为手机、平板电脑、台式机和其他平台提供更一致、更广泛的外观和感觉&#xff0c;帮助设计师们创建易用性和实用性较强的网站和应用程序。这个概念基于一个不断更新的公开文档。该…