摘要:我們找到了許多有趣的工具和組件用來(lái)檢測(cè)狀態(tài)的各個(gè)方面,其中一個(gè)就是在運(yùn)行期通過(guò)反射了解內(nèi)部機(jī)制。由于包含多種的實(shí)現(xiàn),就是供具體實(shí)現(xiàn)比如必須繼承的抽象類(lèi)。調(diào)試器框架是可擴(kuò)展的,這意味著可以通過(guò)繼承這個(gè)抽象類(lèi)來(lái)使用另一個(gè)調(diào)試器。
在日常工作中,我們都習(xí)慣直接使用或者通過(guò)框架使用反射。在沒(méi)有反射相關(guān)硬編碼知識(shí)的情況下,這是Java和Scala編程中使用的類(lèi)庫(kù)與我們的代碼之間進(jìn)行交互的一種主要手段。但是,使用反射僅限于JVM內(nèi)部運(yùn)行的Java和Scala代碼。假使在運(yùn)行期通過(guò)反射既能查看自己的代碼又能看到JVM的代碼,會(huì)有怎樣的效果呢 ?
當(dāng)我們開(kāi)始創(chuàng)建 Takipi 的時(shí)候,試圖尋找一種通過(guò)分析JVM堆內(nèi)存來(lái)進(jìn)行一些底層優(yōu)化的有效方法,比如掃描一個(gè)托管堆塊(managed heap block)的地址空間。我們找到了許多有趣的工具和組件用來(lái)檢測(cè)JVM狀態(tài)的各個(gè)方面,其中一個(gè)就是在運(yùn)行期通過(guò)反射了解JVM內(nèi)部機(jī)制。
譯注: Takipi是一家以色列創(chuàng)業(yè)公司,發(fā)現(xiàn)了一種構(gòu)建后端服務(wù)器網(wǎng)絡(luò)的新方式,可以使用非常簡(jiǎn)化的除錯(cuò)流程幫助企業(yè)定位錯(cuò)誤。
Java可服務(wù)性代理(Serviceablity?Agent,簡(jiǎn)寫(xiě)SA)是最強(qiáng)大和最底層的Java調(diào)試工具之一。這個(gè)強(qiáng)大的工具是HotSpot?JDK自帶的。使用它不僅可以看到堆中的Java對(duì)象,還可以看到內(nèi)部 C++?對(duì)象,包括JVM本身。那才是真正魔法開(kāi)始的地方。
反射的構(gòu)成:在運(yùn)行時(shí)動(dòng)態(tài)檢測(cè)和修改對(duì)象時(shí),無(wú)論使用何種形式的反射都不能缺少兩個(gè)必要信息。第一個(gè)是想要檢測(cè)的對(duì)象引用(或者地址);第二個(gè)是對(duì)象結(jié)構(gòu)描述,包括所有字段的偏移量以及它們的類(lèi)型信息。如果支持動(dòng)態(tài)方法調(diào)用,這個(gè)結(jié)構(gòu)還需要包括類(lèi)方法表 (比如?vtable)的引用,以及每個(gè)方法需要的參數(shù)。
Java反射本身是非常簡(jiǎn)單的,通過(guò)反射獲取目標(biāo)對(duì)象的引用與獲取其它方式一樣。使用 Object.getClass?通用方法(最開(kāi)始從類(lèi)的字節(jié)碼中加載)獲取字段和方法結(jié)構(gòu)。真正的問(wèn)題是:如何反射JVM本身?
反射的關(guān)鍵:令人驚奇的是, JVM通過(guò)一套公開(kāi)的輸出符號(hào)暴露了其內(nèi)部的類(lèi)型系統(tǒng)。?這些符號(hào)給可服務(wù)性代理(或其他工具)提供了訪問(wèn)JVM內(nèi)部類(lèi)系統(tǒng)結(jié)構(gòu)和地址的方法。通過(guò)這些符號(hào),幾乎可以檢測(cè)到JVM內(nèi)部在最底層運(yùn)行機(jī)制的所有方面,包括諸如原始堆地址、線程/棧地址以及編譯器內(nèi)部狀態(tài)等。
反射實(shí)戰(zhàn):為了增加對(duì)這種方式的了解,啟動(dòng)可服務(wù)性代理的HotSpot調(diào)試程序界面,可以看到正在運(yùn)行的一些功能。將 sun.jvm.hotspot.HSDB 作為主類(lèi)參數(shù)啟動(dòng) sa-jdi.jar,可以看到與其它JVM功能強(qiáng)大調(diào)試工具,例如?jmap、jinfo?和?jstack等,一樣的底層信息。
具體實(shí)現(xiàn):讓我們仔細(xì)了解這些由JVM提供的功能。這種方法的基礎(chǔ)是由?jvm 函數(shù)庫(kù)公開(kāi)導(dǎo)出的 gHotSpotVMStructs?結(jié)構(gòu)。這個(gè)結(jié)構(gòu)暴露了JVM內(nèi)部的類(lèi)型系統(tǒng),也為我們提供了可以開(kāi)始反射的根對(duì)象地址??梢酝ㄟ^(guò)?JNI?或者?JNA訪問(wèn)這個(gè)符號(hào),調(diào)用方式與訪問(wèn)動(dòng)態(tài)鏈接的操作系統(tǒng)公開(kāi)系統(tǒng)庫(kù)符號(hào)一樣。
接下來(lái)問(wèn)題就變成:怎樣解析由?gHotSpotVMStructs 符號(hào)中地址的數(shù)據(jù)?從下表中可以看到,JVM不僅暴露了類(lèi)型系統(tǒng)的地址以及根對(duì)象地址,還提供一些額外的符號(hào)用來(lái)解析需要的數(shù)據(jù)值。這些數(shù)據(jù)包括已定位的類(lèi)描述符及其二進(jìn)制偏移量。
清單(manifest): gHotSpotVMStructs 結(jié)構(gòu)指向了類(lèi)及其字段的一個(gè)列表,每個(gè)類(lèi)提供了一個(gè)字段列表。對(duì)于每個(gè)字段,該結(jié)構(gòu)提供了它的名稱(chēng)、類(lèi)型以及是否靜態(tài)字段。如果是一個(gè)靜態(tài)字段,這個(gè)結(jié)構(gòu)還會(huì)提供目標(biāo)對(duì)象的訪問(wèn)地址。該地址可用作反射JVM內(nèi)部特定組件的根,包括諸如編譯器、線程處理或者收集堆系統(tǒng)等。
可以從這里檢出 Hotspot?JDK 中可服務(wù)性代理用來(lái)解析?gHotSpotVMStructs 的實(shí)際算法。
實(shí)例:既然已經(jīng)大概了解了這些功能,現(xiàn)在看一些由該接口訪問(wèn)數(shù)據(jù)類(lèi)型的示例。那些編寫(xiě)SA代理的人,在為gHotSpotVMStructs表中的類(lèi)創(chuàng)建Java封裝時(shí)克服了很多麻煩。在訪問(wèn)大多數(shù)內(nèi)部系統(tǒng)時(shí),這些封裝提供一套簡(jiǎn)潔的API,不僅類(lèi)型安全而且還隱藏了一些解析數(shù)據(jù)所必須的二進(jìn)制工作。
為了更好地了解這些API提供的強(qiáng)大功能,下面是其中的一些底層類(lèi)介紹:
VM?是一個(gè)單例類(lèi),它暴露了JVM的許多內(nèi)部系統(tǒng),比如線程系統(tǒng)、內(nèi)存管理以及集合功能??梢园阉?dāng)作JVM許多子系統(tǒng)的一個(gè)入口。當(dāng)你研究API時(shí),可以從這里入手。
JavaThread?可以讓你從JVM角度理解一個(gè)Java線程,?同時(shí)還有(編譯后的、經(jīng)過(guò)解釋的和本地)幀位置和類(lèi)型的深入信息以及實(shí)際的本地堆棧和CPU寄存器信息。
CollectedHeap?可以讓你研究收集堆(collected heap)的原始內(nèi)容。由于HotSpot包含多種GC的實(shí)現(xiàn),CollectedHeap 就是供具體實(shí)現(xiàn)比如?ParallelScavengeHeap 必須繼承的抽象類(lèi)。每一個(gè)都提供包含Java對(duì)象實(shí)際地址的一組內(nèi)存區(qū)域。
當(dāng)你在看每個(gè)類(lèi)的實(shí)現(xiàn)時(shí),你會(huì)發(fā)現(xiàn)本質(zhì)上它們就是使用類(lèi)似反射API來(lái)查看JVM內(nèi)存硬編碼的包裝器。
C++中的反射:這些Java包裝器中的每一個(gè)都被設(shè)計(jì)為JVM內(nèi)部一個(gè)近乎完整的C++類(lèi)鏡像。我們知道,C++?沒(méi)有原生的反射能力,這就讓我們產(chǎn)生了一個(gè)疑問(wèn):如何在這之間建立的紐帶?
答案就是JVM開(kāi)發(fā)者所了一些非常特別的事情。經(jīng)過(guò)一系列C++宏指令以及大量的艱苦工作,HotSpot?開(kāi)發(fā)小組手動(dòng)將許多內(nèi)部C++類(lèi)的字段結(jié)構(gòu)映射并加載到全局的 gHotSpotVMStructs 中。這個(gè)過(guò)程就是可以從外部進(jìn)行反射的原因。在JVM編譯期間確定了實(shí)際字段偏移量的值和布局,幫助并確保了輸出結(jié)構(gòu)兼容JVM目標(biāo)操作系統(tǒng)。
跨進(jìn)程連接:可服務(wù)性代理有另外一個(gè)強(qiáng)大的功能值得我們關(guān)注。從進(jìn)程外反射一個(gè)內(nèi)部運(yùn)行的JVM是SA框架提供的超酷功能之一,通過(guò)把可服務(wù)性代理附加到目標(biāo)JVM作為一個(gè)操作系統(tǒng)級(jí)別的調(diào)試器可以實(shí)現(xiàn)這一功能。由于 SA?代理框架是依賴(lài)于操作系統(tǒng)的,對(duì)于?Linux 會(huì)利用 gdb?調(diào)試器進(jìn)行連接,對(duì)于Windows 則會(huì)使用winDbg (這意味著需要Windows?調(diào)試工具)。調(diào)試器框架是可擴(kuò)展的,這意味著可以通過(guò)繼承?DebuggerBase?這個(gè)抽象類(lèi)來(lái)使用另一個(gè)調(diào)試器。
一旦建立了調(diào)試器的連接,gHotSpotVMStruct的返回地址值會(huì)傳回到調(diào)試器進(jìn)程,這樣可以(借助操作系統(tǒng))開(kāi)始檢測(cè)甚至是修改目標(biāo)JVM的內(nèi)部對(duì)象系統(tǒng)。這就是HSDB如何實(shí)現(xiàn)連接并調(diào)試一個(gè)目標(biāo)JVM,包括Java代碼和JVM代碼。
希望本文能夠勾起你的興趣。 以我個(gè)人觀點(diǎn)來(lái)看,這個(gè)架構(gòu)是我最喜歡的JVM設(shè)計(jì)之一?。它的優(yōu)雅和開(kāi)放絕對(duì)令人驚嘆。在我們構(gòu)建?Takipi 實(shí)時(shí)編碼模塊時(shí)超級(jí)有用,?所以要感謝它的設(shè)計(jì)者。
你是否已經(jīng)在代碼中使用了這些API?非常樂(lè)意在下面的評(píng)論中聽(tīng)到它,或者回答你們的任何問(wèn)題。
原文 Using Reflection To Look Inside The JVM at Run-time
翻譯 黃飛飛
via importnew
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69771.html
反射機(jī)制與原理筆記 聲明 文章均為本人技術(shù)筆記,轉(zhuǎn)載請(qǐng)注明出處https://segmentfault.com/u/yzwall 反射機(jī)制 反射:當(dāng)程序無(wú)法獲知對(duì)象類(lèi)型時(shí),在運(yùn)行期間動(dòng)態(tài)獲取類(lèi)的所有屬性和方法,這種動(dòng)態(tài)獲取類(lèi)信息和動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱(chēng)為反射機(jī)制;反射機(jī)制實(shí)現(xiàn):Class類(lèi)與java.lang.reflect類(lèi)庫(kù)一起實(shí)現(xiàn)機(jī)制,java.lang.reflect類(lèi)庫(kù)包含F(xiàn)ield...
摘要:說(shuō)明我們創(chuàng)建時(shí)傳入了一個(gè)對(duì)象。在這個(gè)對(duì)象里我們調(diào)用了被代理的那個(gè)對(duì)象的方法,并且在其前后附加了方法。切面在指定的連接點(diǎn)被織入到目標(biāo)對(duì)象中。因?yàn)椴捎脛?dòng)態(tài)代理,所以是在運(yùn)行期完成織入。中的代理一個(gè)類(lèi)被織入增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類(lèi) showImg(/img/bVCPFw?w=853&h=426); showImg(/img/bVCPFV?w=636&h=235); 說(shuō)明:圖片是我其它地方...
摘要:中的反射反射能夠分析類(lèi)所擁有的能力的程序稱(chēng)為反射。獲取類(lèi)的名稱(chēng)獲取類(lèi)的修飾符獲取類(lèi)所在的包獲取父類(lèi)的屬性獲取類(lèi)的構(gòu)造器等等獲得的構(gòu)造器的使用獲取構(gòu)造器有兩種方法。 Java中的反射 反射:能夠分析類(lèi)所擁有的能力的程序稱(chēng)為反射。 反射的作用 當(dāng)我們?cè)谑褂靡粋€(gè)已有的類(lèi)的時(shí)候,在主代碼的main()方法中使用別的已有的類(lèi)的時(shí)候,如果被使用的類(lèi)發(fā)生了改變,那么導(dǎo)致我們的main()方法中...
摘要:可實(shí)現(xiàn)單例模式代碼塊初始化靜態(tài)變量,只被執(zhí)行一次內(nèi)部類(lèi)不能與外部類(lèi)重名,只能訪問(wèn)外部類(lèi)靜態(tài)數(shù)據(jù)包括私有多分支選擇整型或字符類(lèi)型變量或整數(shù)表達(dá)式開(kāi)始支持。 前言 大學(xué)期間接觸 Java 的時(shí)間也不短了,不論學(xué)習(xí)還是實(shí)習(xí),都讓我發(fā)覺(jué)基礎(chǔ)的重要性。互聯(lián)網(wǎng)發(fā)展太快了,各種框架各種技術(shù)更新迭代的速度非???,可能你剛好掌握了一門(mén)技術(shù)的應(yīng)用,它卻已經(jīng)走在淘汰的邊緣了。 而學(xué)習(xí)新技術(shù)總要付出一定的時(shí)間...
摘要:為了減少在中創(chuàng)建的字符串的數(shù)量,字符串類(lèi)維護(hù)了一個(gè)字符串常量池。但是當(dāng)執(zhí)行了方法后,將指向字符串常量池中的那個(gè)字符串常量。由于和都是字符串常量池中的字面量的引用,所以。究其原因,是因?yàn)槌A砍匾4娴氖且汛_定的字面量值。 String,是Java中除了基本數(shù)據(jù)類(lèi)型以外,最為重要的一個(gè)類(lèi)型了。很多人會(huì)認(rèn)為他比較簡(jiǎn)單。但是和String有關(guān)的面試題有很多,下面我隨便找兩道面試題,看看你能不能...
閱讀 3268·2021-11-24 09:39
閱讀 3217·2021-10-21 09:38
閱讀 2424·2019-08-29 15:28
閱讀 3768·2019-08-26 12:23
閱讀 2645·2019-08-26 12:19
閱讀 1381·2019-08-23 12:44
閱讀 2154·2019-08-23 12:02
閱讀 1078·2019-08-22 17:05