一、JVMTI簡(jiǎn)介
JVMTI(JVM tool interface)位于jpda最底層,JVMTI是JPDA中的一環(huán)
JPDA叫做Java 平臺(tái)調(diào)試架構(gòu)(Java Platform Debugger Architecture)
Java平臺(tái)調(diào)試體系JPDA(Java PlatformDebugger Architecture)。它是Java虛擬機(jī)為調(diào)試和監(jiān)控虛擬機(jī)專門提供的一套接口。如下圖所示,JPDA被抽象為三層實(shí)現(xiàn)。其中JVMTI就是JVM對(duì)外暴露的接口。JDI是實(shí)現(xiàn)了JDWP通信協(xié)議的客戶端,調(diào)試器通過(guò)它和JVM中被調(diào)試程序通信。
JVMTI 本質(zhì)上是在JVM內(nèi)部的許多事件進(jìn)行了埋點(diǎn)。通過(guò)這些埋點(diǎn)可以給外部提供當(dāng)前上下文的一些信息。甚至可以接受外部的命令來(lái)改變下一步的動(dòng)作。外部程序一般利用C/C++實(shí)現(xiàn)一個(gè)JVMTIAgent,在Agent里面注冊(cè)一些JVM事件的回調(diào)。當(dāng)事件發(fā)生時(shí)JVMTI調(diào)用這些回調(diào)方法。Agent可以在回調(diào)方法里面實(shí)現(xiàn)自己的邏輯。JVMTIAgent是以動(dòng)態(tài)鏈接庫(kù)的形式被虛擬機(jī)加載的。
這三個(gè)規(guī)范把調(diào)試過(guò)程分解成幾個(gè)概念:調(diào)試者(debugger)、被調(diào)試者(debuggee)、以及它們中間的通信器
JVMTI(Java Virtual Machine Tool Interface)即指 Java 虛擬機(jī)工具接口,它是一套由虛擬機(jī)直接提供的 native 接口,它處于整個(gè) JPDA 體系的最底層,所有調(diào)試功能本質(zhì)上都需要通過(guò) JVMTI 來(lái)提供。
通過(guò)這些接口,開(kāi)發(fā)人員不僅調(diào)試在該虛擬機(jī)上運(yùn)行的 Java 程序,還能查看它們運(yùn)行的狀態(tài),設(shè)置回調(diào)函數(shù),控制某些環(huán)境變量,從而優(yōu)化程序性能。
JVMTI 本質(zhì)上是在 JVM 內(nèi)部的許多事件進(jìn)行了埋點(diǎn)。通過(guò)這些埋點(diǎn)可以給外部提供當(dāng)前上下文的一些信息。甚至可以接受外部的命令來(lái)改變下一步的動(dòng)作。
外部程序一般利用C/C++實(shí)現(xiàn)一個(gè)JVMTIAgent,在Agent里面注冊(cè)一些JVM事件的回調(diào)。當(dāng)事件發(fā)生時(shí)JVMTI調(diào)用這些回調(diào)方法。Agent可以在回調(diào)方法里面實(shí)現(xiàn)自己的邏輯。JVMTIAgent是以動(dòng)態(tài)鏈接庫(kù)的形式被虛擬機(jī)加載的。
JVMTI的歷史
JVMTI 的前身是 JVMDI(Java Virtual Machine Debug Interface)和 JVMPI(Java Virtual Machine Profiler Interface),它們?cè)瓉?lái)分別被用于提供調(diào)試 Java 程序以及 Java 程序調(diào)節(jié)性能的功能。在 J2SE 5.0 之后 JDK 取代了JVMDI 和 JVMPI 這兩套接口,JVMDI 在最新的 Java SE 6 中已經(jīng)不提供支持,而 JVMPI 也計(jì)劃在 Java SE 7 后被徹底取代。
JVMTI的功能
JVMTI處于整個(gè)JPDA 體系的最底層,所有調(diào)試功能本質(zhì)上都需要通過(guò) JVMTI 來(lái)提供。從大的方面來(lái)說(shuō),JVMTI 提供了可用于 debug 和profiler 的接口;同時(shí),在 Java 5/6 中,虛擬機(jī)接口也增加了監(jiān)聽(tīng)(Monitoring),線程分析(Thread analysis)以及覆蓋率分析(Coverage Analysis)等功能。從小的方面來(lái)說(shuō)包含了虛擬機(jī)中線程、內(nèi)存、堆、棧、類、方法、變量,事件、定時(shí)器處理等等諸多功能。具體可以參考o(jì)racle 的文檔:https://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/jvmti.html。通過(guò)這些接口,開(kāi)發(fā)人員不僅可以調(diào)試在該虛擬機(jī)上運(yùn)行的 Java 程序,還能查看它們運(yùn)行的狀態(tài),設(shè)置回調(diào)函數(shù),控制某些環(huán)境變量,從而優(yōu)化程序性能。
JVMTI的實(shí)現(xiàn)
JVMTI 并不一定在所有的 Java 虛擬機(jī)上都有實(shí)現(xiàn),不同的虛擬機(jī)的實(shí)現(xiàn)也不盡相同。不過(guò)在一些主流的虛擬機(jī)中,比如 Sun 和 IBM,以及一些開(kāi)源的如Apache Harmony DRLVM 中,都提供了標(biāo)準(zhǔn) JVMTI 實(shí)現(xiàn)。
JVMTI可以用來(lái)實(shí)現(xiàn)哪些黑科技
使用JVMTI對(duì)class文件加密
有時(shí)一些涉及到關(guān)鍵技術(shù)的class文件或者jar包我們不希望對(duì)外暴露,因而需要進(jìn)行加密。使用一些常規(guī)的手段(例如使用混淆器或者自定義類加載器)來(lái)對(duì)class文件進(jìn)行加密很容易被反編譯。反編譯后的代碼雖然增加了閱讀的難度,但花費(fèi)一些功夫也是可以讀懂的。使用JVMTI我們可以將解密的代碼封裝成.dll,或.so 文件。這些文件想要反編譯就很麻煩了,另外還能加殼。解密代碼不能被破解,從而也就保護(hù)了我們想要加密的class文件。
使用JVMTI實(shí)現(xiàn)應(yīng)用性能監(jiān)控(APM)
在微服務(wù)大行其道的環(huán)境下,分布式系統(tǒng)的邏輯結(jié)構(gòu)變得越來(lái)越復(fù)雜。這給系統(tǒng)性能分析和問(wèn)題定位帶來(lái)了非常大的挑戰(zhàn)。基于JVMTI的APM能夠解決分布式架構(gòu)和微服務(wù)帶來(lái)的監(jiān)控和運(yùn)維上的挑戰(zhàn)。APM通過(guò)匯聚業(yè)務(wù)系統(tǒng)各處理環(huán)節(jié)的實(shí)時(shí)數(shù)據(jù),分析業(yè)務(wù)系統(tǒng)各事務(wù)處理的交易路徑和處理時(shí)間,實(shí)現(xiàn)對(duì)應(yīng)用的全鏈路性能監(jiān)測(cè)。開(kāi)源的Pinpoint, ZipKin, Hawkular,商業(yè)的AppDynamics,OneAPM,Google Dapper等都是個(gè)中好手。
產(chǎn)品運(yùn)行時(shí)錯(cuò)誤監(jiān)測(cè)及調(diào)試
想要看生產(chǎn)環(huán)境的異常,最原始的方式是登錄到生產(chǎn)環(huán)境的機(jī)器查看日志。稍微高級(jí)一點(diǎn)的方式是通過(guò)日志監(jiān)控或者APM等工具將異常采集上來(lái)。但是這些手段都有許多明顯的缺點(diǎn)。首先,不是所有的異常都會(huì)被打印到日志中,有些異常可能被代碼吃掉了;其次,打印異常的時(shí)候通常只有異常堆棧信息,異常發(fā)生時(shí)上下文的變量值很難獲取到(除非有經(jīng)驗(yàn)的程序員將其打印出來(lái)了),而這些信息對(duì)定位異常的原因至關(guān)重要。基于JVMTI可以開(kāi)發(fā)出一款工具來(lái)時(shí)事監(jiān)控生產(chǎn)環(huán)境的異常。這方面有一款成熟的商業(yè)軟件OverOps,其有三個(gè)主要的功能:1. 采集到所有的異常,包括try catch之后沒(méi)有打印出來(lái)的異常;2. 可以采集到異常發(fā)生時(shí)上下文所有變量的值;3. 可以將異常發(fā)生的堆棧對(duì)應(yīng)的源代碼采集展示出來(lái),從而在一個(gè)系統(tǒng)上就可以看代碼定位問(wèn)題,不需要打開(kāi)ide調(diào)試源代碼。
JAVA程序的調(diào)試(debug)。
一般JAVA的IDE都自帶了調(diào)試工具。例如Eclipse的調(diào)試器相信大部分人都使用過(guò)。它的調(diào)試器org.eclipse.jdt.debug插件底層就是調(diào)用的JVMTI來(lái)實(shí)現(xiàn)的。不僅如此,隨著服務(wù)云化的發(fā)展,google甚至推出了云端調(diào)試工具cloud debugger。它時(shí)一個(gè)web應(yīng)用,可以直接對(duì)生產(chǎn)環(huán)境進(jìn)行遠(yuǎn)程調(diào)試,不需要重啟或者中斷服務(wù)。阿里也有類似的工具Zdebugger。
JAVA程序的診斷(profile)。
當(dāng)出現(xiàn)cpu使用率過(guò)高、線程死鎖等問(wèn)題時(shí),需要使用一些JAVA性能剖析或者診斷工具來(lái)分析具體的原因。例如Alibaba開(kāi)源的Java診斷工具Arthas,深受開(kāi)發(fā)者喜愛(ài)。Arthas的功能十分強(qiáng)大,它可以查看或者動(dòng)態(tài)修改某個(gè)變量的值、統(tǒng)計(jì)某個(gè)方法調(diào)用鏈上的耗時(shí)、攔截方法前后,打印參數(shù)值和返回值,以及異常信息等。
熱加載
熱加載指的是在不重啟虛擬機(jī)的情況下重新加載一些class。熱加載可以使本地調(diào)試代碼非常節(jié)省時(shí)間,不用每次更新代碼都重啟一邊程序。同時(shí),在一線不方便重啟的線上環(huán)境也能派上用場(chǎng)。這方面的代表產(chǎn)品有商業(yè)產(chǎn)品JRebel等。JRebel能夠?qū)?yīng)用中的任何class起作用。