日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

.class文件转换.java_Java中的动态链接VS操作系统动态链接

發(fā)布時(shí)間:2025/3/8 windows 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .class文件转换.java_Java中的动态链接VS操作系统动态链接 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在操作系統(tǒng)OS中為了優(yōu)化內(nèi)存的使用會(huì)采用一種動(dòng)態(tài)鏈接方式,一個(gè)文件想要在操作系統(tǒng)中運(yùn)行必須經(jīng)過(guò)編譯、匯編譯、鏈接、裝載等步驟。可以參考Java程序是怎么跑起來(lái)的。本篇主要講解Java棧幀中動(dòng)態(tài)鏈接部分與操作系統(tǒng)的的動(dòng)態(tài)鏈接的區(qū)別與聯(lián)系

操縱系統(tǒng)為什么需要?jiǎng)討B(tài)鏈接

OS是向下統(tǒng)一管理機(jī)器硬件、向上給各個(gè)應(yīng)用程序提供統(tǒng)一的系統(tǒng)調(diào)用的程序。其中對(duì)內(nèi)存的管理也是重頭戲,以下是32位Linux操作系統(tǒng)中虛擬內(nèi)存的空間分配圖

每一個(gè)應(yīng)用程序看到的內(nèi)存就是這樣的一段虛擬內(nèi)存空間。應(yīng)用程序的代碼指令就存儲(chǔ)在.text段也叫代碼段,只讀。.data段也叫數(shù)據(jù)段用于存儲(chǔ)程序的靜態(tài)變量、全局變量,可讀可寫(xiě)

在應(yīng)用程序的運(yùn)行中除了運(yùn)行自身的代碼指令外還需要加載一些系統(tǒng)的公共庫(kù),比如用于網(wǎng)絡(luò)收發(fā)的socket庫(kù)等。在windows中,這些共享庫(kù)以.dll(Dynamic-Link Libary 動(dòng)態(tài)鏈接庫(kù))結(jié)尾,在Linux中以.so(Shared Object)結(jié)尾。加載這些共享庫(kù)可進(jìn)行對(duì)系統(tǒng)資源的調(diào)用

靜態(tài)鏈接

當(dāng)應(yīng)用程序代碼經(jīng)歷鏈接過(guò)程生成可執(zhí)行文件時(shí),每鏈接一個(gè)共享庫(kù)就將共享庫(kù)代碼復(fù)制一份進(jìn)應(yīng)用程序的可執(zhí)行文件中,因此有多少應(yīng)用程序調(diào)用同一個(gè)共享庫(kù)文件,該共享庫(kù)文件中的代碼就在內(nèi)存中加載多少份

動(dòng)態(tài)鏈接

在沒(méi)用動(dòng)態(tài)鏈接前,系統(tǒng)確實(shí)是采用靜態(tài)鏈接的方式鏈接共享庫(kù),但是發(fā)現(xiàn)對(duì)內(nèi)存的使用是一種極大的浪費(fèi),因此動(dòng)態(tài)鏈接孕育而生。為了達(dá)到各個(gè)應(yīng)用程序只加載同一個(gè)共享庫(kù)但內(nèi)存只存在一份共享庫(kù)代碼,動(dòng)態(tài)鏈接首先解決的技術(shù)問(wèn)題是地址無(wú)關(guān)性

PLT、GOT表解決地址無(wú)關(guān)性

我們都知道程序代碼指令加載進(jìn)內(nèi)存的代碼段是可執(zhí)行只讀的,無(wú)法動(dòng)態(tài)的修改代碼指令。那么當(dāng)共享庫(kù)載入內(nèi)存中時(shí)是怎么被各個(gè)不同的應(yīng)用程序找到的呢?

其實(shí)在應(yīng)用程序的可執(zhí)行文件加載進(jìn)內(nèi)存后,該程序的內(nèi)存數(shù)據(jù)段(.data)存在一張GOT(Global Offset Table)全局偏移表,GOT表中,當(dāng)有需要引用共享庫(kù)地址的方法指令,都會(huì)查詢 GOT,根據(jù)GOT表找到共享庫(kù)方法指令的地址位置并調(diào)用。因?yàn)镚OT存在于數(shù)據(jù)段,因此當(dāng)共享庫(kù)發(fā)生變化時(shí),應(yīng)用程序也不需要重新編譯,可以直接動(dòng)態(tài)的改變GOT表中的虛擬內(nèi)存,從而找到最新的共享庫(kù)。

共享庫(kù)載入實(shí)際的物理內(nèi)存,雖然物理內(nèi)存不會(huì)變,但是每個(gè)應(yīng)用程序看到的虛擬內(nèi)存不一樣,所以共享庫(kù)在不同的應(yīng)用程序中的虛存地址是不一樣的,好在每個(gè)應(yīng)用程序都擁有自己的GOT表,能夠準(zhǔn)確的記錄了共享庫(kù)的位置。這也就達(dá)到了地址無(wú)關(guān)性。

PLT(Procedure Link Table)程序鏈接表存在于內(nèi)存的代碼段中,主要是用于延遲綁定,我們可以將其理解為跳表。應(yīng)用程序先是調(diào)用PLT表中查詢需要調(diào)用的GOT表的地址位置,跳到GOT表后查詢出共享庫(kù)的虛存,然后再去調(diào)用共享庫(kù)方法。因?yàn)楹芏鄤?dòng)態(tài)裝載的函數(shù)庫(kù)都是不會(huì)被實(shí)際調(diào)用到的,而共享庫(kù)中存在非常多的函數(shù),因此采用PLT可達(dá)到延遲加載。

像動(dòng)態(tài)鏈接這樣通過(guò)修改“地址數(shù)據(jù)”來(lái)進(jìn)行間接跳轉(zhuǎn),去調(diào)用一開(kāi)始不能確定位置代碼的思路,Java中的多態(tài)也采用了這種思想。

Java棧幀中的動(dòng)態(tài)鏈接

以前的文章中解釋了棧幀(拆解棧幀中本地變量表),其中動(dòng)態(tài)鏈接也是組成棧幀的一部分。在上面對(duì)OS的解釋中,動(dòng)態(tài)鏈接是一種技術(shù)名稱,在Java棧幀這里怎么就成了一個(gè)實(shí)體了呢?其實(shí)根據(jù)Java虛擬機(jī)對(duì)動(dòng)態(tài)鏈接的描述,翻譯成中文就是一個(gè)【引用】,那么棧幀存在的這個(gè)【引用】是干什么的呢?

在解釋這個(gè)【引用】的作用之前,還是先說(shuō)明一點(diǎn),Java棧幀中的動(dòng)態(tài)鏈接的目的其實(shí)跟OS的是一樣的,都是為了節(jié)省內(nèi)存空間,知道這個(gè)目的后我們?cè)僬f(shuō)明為什么可以節(jié)省。也因此在看JVM的時(shí)候,我總是會(huì)與OS做類比。

這個(gè)【引用】在虛機(jī)規(guī)范的解釋為指向運(yùn)行時(shí)常量池的方法引用。每當(dāng)棧幀中調(diào)用其他方法時(shí)都會(huì)存在一個(gè)【引用】。在.class文件中所有的變量和方法引用都是符號(hào)引用(Symbolic Reference)也就是下面字節(jié)碼中的 #數(shù)字。比如下面用javap反編譯的.class文件中的Constant pool。

public?class?com.ethan.chapter02.Test02LocalVariablesminor?version:?0major?version:?52flags:?ACC_PUBLIC,?ACC_SUPERConstant?pool:
???#1?=?Methodref??????????#7.#28?????????//?java/lang/Object."":()V
???#2?=?Class??????????????#29????????????//?com/ethan/chapter02/Test02LocalVariables
???#3?=?Methodref??????????#2.#28?????????//?com/ethan/chapter02/Test02LocalVariables."":()V
???#4?=?Methodref??????????#2.#30?????????//?com/ethan/chapter02/Test02LocalVariables.test3:()V
???#5?=?Long???????????????100l
???#7?=?Class??????????????#31????????????//?java/lang/Object
???#8?=?Utf8???????????????
???#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??????????//?"":()V
??#29?=?Utf8???????????????com/ethan/chapter02/Test02LocalVariables
??#30?=?NameAndType????????#22:#9?????????//?test3:()V
??#31?=?Utf8???????????????java/lang/Object
{public?com.ethan.chapter02.Test02LocalVariables();
????descriptor:?()V
????flags:?ACC_PUBLIC
????Code:
??????stack=1,?locals=1,?args_size=10:?aload_01:?invokespecial?#1??????????????????//?Method?java/lang/Object."":()V4:?return
??????LineNumberTable:
????????line?10:?0
??????LocalVariableTable:
????????Start??Length??Slot??Name???Signature0???????5?????0??this???Lcom/ethan/chapter02/Test02LocalVariables;public?static?void?main(java.lang.String[]);
????descriptor:?([Ljava/lang/String;)V
????flags:?ACC_PUBLIC,?ACC_STATIC
????Code:
??????stack=2,?locals=3,?args_size=10:?new???????????#2??????????????????//?class?com/ethan/chapter02/Test02LocalVariables3:?dup4:?invokespecial?#3??????????????????//?Method?"":()V7:?astore_18:?bipush????????1010:?istore_211:?aload_112:?invokevirtual?#4??????????????????//?Method?test3:()V15:?return
??????LineNumberTable:
????????line?12:?0
????????line?13:?8
????????line?14:?11
????????line?15:?15
??????LocalVariableTable:
????????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:?()V
????flags:?ACC_PUBLIC
????Code:
??????stack=2,?locals=4,?args_size=10:?ldc2_w????????#5??????????????????//?long?100l3:?lstore_14:?bipush????????106:?istore_37:?return
??????LineNumberTable:
????????line?20:?0
????????line?21:?4
????????line?22:?7
??????LocalVariableTable:
????????Start??Length??Slot??Name???Signature0???????8?????0??this???Lcom/ethan/chapter02/Test02LocalVariables;4???????4?????1?????q???J7???????1?????3?????a???I
}

在.class文件中的常量池會(huì)隨著文件被加載而轉(zhuǎn)換進(jìn)JVM中的運(yùn)行時(shí)常量池中。由于存在了這些【符號(hào)引用】,可以使用Java層面的動(dòng)態(tài)鏈接技術(shù),將這些符號(hào)引用轉(zhuǎn)換為調(diào)用方法的直接引用。比如字節(jié)碼中的 invokevirtual指令就能夠支持動(dòng)態(tài)鏈接。

在類加載子系統(tǒng)中,一個(gè).class文件被加載進(jìn)JVM共需要經(jīng)歷3步驟,加載-鏈接-初始化。而在鏈接階段中的第三步【解析】的目的就是將常量池內(nèi)的符號(hào)引用轉(zhuǎn)換為直接引用的過(guò)程,也就是動(dòng)態(tài)鏈接產(chǎn)生的過(guò)程。

我們類比一下OS的動(dòng)態(tài)鏈接與Java的動(dòng)態(tài)鏈接。Java的.class文件類比于OS的每一個(gè)應(yīng)用程序的可執(zhí)行文件,.class文件中的常量池類比于GOT表,java的運(yùn)行時(shí)常量池類比于共享庫(kù)。java產(chǎn)生動(dòng)態(tài)的鏈接是在.class的解析階段,根據(jù).class文件中的符號(hào)引用去查詢常量池然后,將.class文件中的符號(hào)引用轉(zhuǎn)換為直接應(yīng)用,并存于棧幀中。

因?yàn)樵诩虞d不同的.class文件時(shí),都可能調(diào)用相同的常量或者方法,所以只需要在運(yùn)行時(shí)常量池存儲(chǔ)一份,然后記錄其直接引用即可,因此節(jié)省了空間。

解釋完Java層面的動(dòng)態(tài)鏈接我們也就能解釋Java多態(tài)的實(shí)現(xiàn)過(guò)程了,在Java源代碼編譯期間方法的重寫(xiě)導(dǎo)致無(wú)法確認(rèn)出調(diào)用方法的真正位置,只有在運(yùn)行時(shí)將符號(hào)引用轉(zhuǎn)為為直接應(yīng)用采用確定方法的位置。這個(gè)過(guò)程也就是在【解析】階段實(shí)現(xiàn)的。

這種編譯時(shí)期無(wú)法確定方法的調(diào)用位置,只能夠在程序運(yùn)行期根據(jù)實(shí)際的類型綁定相關(guān)方法,這種綁定方式也就被稱之為晚期綁定。

總結(jié)

以上是生活随笔為你收集整理的.class文件转换.java_Java中的动态链接VS操作系统动态链接的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。