色欧美4477福利网在线观看,亚洲国产AV一区二区污污污,精品欧美一区二区三区,免费人成在线观看欧美精品

    青島思途教育

    青島思途教育

    • 青島思途教育為學員未來10年職業(yè)發(fā)展奠定基礎,搭建平臺。
    • 青島思途是一家集軟件開發(fā)和IT教育實訓為一體的科技實訓學校。
    • 青島思途教育力爭為全國學員提供技術實訓和服務

    400-882-1633

    全國學習專線 8:00-22:00

    在Java中運用修復Bug的小技巧

    JAVA 23837已閱讀 2020-03-18 13:46:30
    導讀 Java是一種編程語言,相信很多熟知Java的同學都會碰到一種常見的問題那就是在Java中運用動態(tài)掛載的過程中經(jīng)常會出現(xiàn)很多bug,那么針對性這些我們常見的bug我們應該如何解決呢?
    在Java中運用修復Bug的小技巧
    文章介紹:

      Java是一種編程語言,相信很多熟知Java的同學都會碰到一種常見的問題那就是在Java中運用動態(tài)掛載的過程中經(jīng)常會出現(xiàn)很多bug,那么針對性這些我們常見的bug我們應該如何解決呢?以下是小編整理的遇到bug時候要怎么解決的資料,一起來看看吧!

      大多數(shù)JVM具備Java的HotSwap特性,大部分開發(fā)者認為它僅僅是一個調試工具。利用這一特性,有可能在不重啟Java進程條件下,改變Java方法的實現(xiàn)。典型的例子是使用IDE來編碼。然而HotSwap可以在生產(chǎn)環(huán)境中實現(xiàn)這一功能。通過這種方式,不用停止運行程序,就可以擴展在線的應用程序,或者在運行的項目上修復小的錯誤。這篇文章中,我將演示動態(tài)綁定、應用運行期代碼變化進行綁定、介紹一些工具API以及ByteBuddy庫,這個庫提供了一些API代碼改變更方便。


      假設有一個正在運行的應用程序,通過校驗HTTP請求中的X-Priority頭部,來執(zhí)行服務器的特殊處理。該校驗使用下面的工具類來實現(xiàn):

      classHeaderUtility{staticbooleanisPriorityCall(HttpServletRequestrequest){returnrequest.getHeader("X-Pirority")!=null;}}

      你發(fā)現(xiàn)錯誤了嗎?這樣的錯誤很常見,尤其是在測試代碼中常量值分解為靜態(tài)字段重用。在不太理想的情況下,這個錯誤只會在產(chǎn)品被安裝的時候才被發(fā)現(xiàn),其中頭通過另外一個應用生成并沒有拼寫錯誤。

      修復這樣的錯誤并不難。在持續(xù)交付的時代,重新部署一個新的版本只需要點擊一下按鈕。但在其他情況下,變更可能就不是那么簡單了,重新部署過程可能比較復雜,其中停機是不允許的,帶著錯誤運行可能會比較好。但HotSwap給我們提供了另外一種選擇:在不重啟應用的前提下進行小幅改動。

    1
    AttachAPI:使用動態(tài)附件來滲透另外一個JVM:

      為了修改一個運行中的Java程序,我們首先需要一種可以同處在運行狀態(tài)的JVM進行通信的方式。因為Java的虛擬機實現(xiàn)是一個受到管理的系統(tǒng),因此擁有進行這些操作的標準API。提問中涉及到的API被稱作attachmentAPI,它是官方Java工具的一部分。使用這個由運行之中的JVM所暴露的API,能讓第二個Java進程來同其進行通信。

      事實上,我們已經(jīng)用到了該API:它已經(jīng)由諸如VisualVM或者JavaMissionControl這樣的調試和模擬工具進行了應用。應用這些附件的API并沒有同日常使用的標準JavaAPI打在一起,而是被打到了一個特殊的文件之中,叫做tools.jar,它只含了一個虛擬機的JDK打發(fā)布版本。更糟糕的是,這個JAR文件的位置并沒有進行設置,它在Windows、Linux,特別是在Macintosh上的VM都存在差別,不光文件的位置,連文件名也各異,有些發(fā)行版上就被叫做classes.jar。最后,IBM甚至決定對這個JAR中含的一些類的名稱進行修改,將所有com.sun類挪到com.ibm命名空間之中,又添了一個亂子。在Java9中,亂糟糟的狀態(tài)才最終得以清理,tools.jar被Jigsaw的模塊jdk.attach所替代。
      在對API的JAR(或者模塊)進行了定位之后,我們就該讓其對附件進程可用。在OpenJDK上,被用來連接到另外一個JVM的類叫做VirtualMachine,它向任何由位于同一臺物理機器上的JDK或者是一個普通的HtpSpotJVM所運行的VM提供了一個入口點。在通過進程id附加到另外一臺虛擬機上之后,我們就能夠在目標VM指定的一個線程中運行一個JAR文件:

      //thefollowingstringsmustbeprovidedbyusStringprocessId=processId();StringjarFileName=jarFileName();VirtualMachinevirtualMachine=VirtualMachine.attach(processId);try{virtualMachine.loadAgent(jarFileName,"World!");}finally{virtualMachine.detach();}

      在收到一個JAR文件之后,目標虛擬機會查看該JAR的程序清單描述文件(manifest),并定位處在Premain-Class屬性之下的類。這非常類似于VM執(zhí)行一個主方法的方式。有了一個Java代理,VM和指定的進程id就可以查找到一個名為agentmain的方法,該方法可以由指定線程中的遠程進程來執(zhí)行:

      publicclassHelloWorldAgent{publicstaticvoidagentmain(Stringarg){System.out.println("Hello,"+arg);}}

      使用該API,只要我們知道一個JVM的進程id,就可以來在其上運行代碼,打印出一條Hello,World!消息。甚至有可能同并不熟JDK發(fā)行版一部分的JVM進行通信,只要附加的VM是一個用來訪問tools.jar的JDK安裝程序。

    2
    InstrumentationAPI:修改目標VM的程序:

      到目前來看一切順利。但是除了成功地同目標VM建立起了通信之外,我們還不能夠修改目標VM上的代碼以及BUG。后續(xù)的修改,Java代理可以定義第二參數(shù)來接收一個Instrumentation的實例。稍后要實現(xiàn)的接口提供了向幾個底層方法的訪問途徑,它們中的一個就能夠對已經(jīng)加載的代碼進行修改。

      為了修正“X-Pirority”錯字,我們首先來假設為HeaderUtility引入了一個修復類,叫做typo.fix,就在我們下面所開發(fā)的BugFixAgent后面的代理的JAR文件中。此外,我們需要給予代理通過向manifest文件添加Can-Redefine-Classes:true來替換現(xiàn)有類的能力。有了現(xiàn)在這些東西,我們就可以使用instrumentation的API來對類進行重新定義,該API會接受一對已經(jīng)加載的類以及用來執(zhí)行類重定義的字節(jié)數(shù)組:

      publicclassBugFixAgent{publicstaticvoidagentmain(Stringarg,Instrumentationinst)throwsException{//onlyifheaderutilityisontheclasspath;otherwise,//aclasscanbefoundwithinanyclassloaderbyiterating//overthereturnvalueofInstrumentation::getAllLoadedClassesClass<?>headerUtility=Class.forName("HeaderUtility");//copythecontentsoftypo.fixintoabytearrayByteArrayOutputStreamoutput=newByteArrayOutputStream();try(InputStreaminput=BugFixAgent.class.getResourceAsStream("/typo.fix")){byte[]buffer=newbyte[1024];intlength;while((length=input.read(buffer))!=-1){output.write(buffer,0,length);}}//Applytheredefinitioninstrumentation.redefineClasses(newClassDefinition(headerUtility,output.toByteArray()));}}

      運行上述代碼后,HeaderUtility類會被重定義以對應其修補的版本。對isPrivileged的任何后續(xù)調用現(xiàn)在將讀取正確的頭信息。作為一個小的附加說明,JVM可能會在應用類重定義時執(zhí)行完全的垃圾回收,并且會對受影響的代碼進行重新優(yōu)化。總之,這會導致應用程序性能的短時下降。然而,在大多數(shù)情況下,這是較之完全重啟進程更好的方式。

      當應用代碼更改時,要確保新類定義了與它替換的類完全相同的字段、方法和修飾符。嘗試修改任何此類屬性的類重定義行為都會導致UnsupportedOperationException。現(xiàn)在HotSpot團隊正試圖去掉這個限制。此外,基于OpenJDK的動態(tài)代碼演變虛擬機支持預覽此功能。

    3
    使用ByteBuddy來追蹤內存泄漏:

      一個如上述示例的簡單的BUG修復代理在你熟悉了instrumentation的API的時候是比較容易實現(xiàn)的。只要更加深入一點,也可以在運行代理的時候,無需手動創(chuàng)建附加的class文件,而是通過重寫現(xiàn)有的class來應用更多通用的代碼修改。

    4
    字節(jié)碼操作:

      編譯好的Java代碼所呈現(xiàn)的是一系列字節(jié)碼指令。從這個角度來看,一個Java方法無非就是一個字節(jié)數(shù)組,其每一個字節(jié)都是在表示一個向運行時發(fā)出的指令,或者是最近一個指令的參數(shù)。每個字節(jié)對應其意義的映射在《Java虛擬機規(guī)范》中進行了定義,例如字節(jié)0xB1就是在指示VM從一個帶有void返回類型的方法返回。因此,對字節(jié)碼進行增強就是對一個方法的字節(jié)數(shù)字進行擴展,將我們想要應用的表示額外的業(yè)務邏輯指令含進去。

      當然,逐個字節(jié)的操作會特別麻煩,而且容易出錯。為了避免手工的處理,許多的庫都提供了更高級一點的API,使用它們不需要我們直接同Java字節(jié)碼打交道。這樣的庫其中就有一個叫做ByteBuddy(當然我就是該庫的作者)。它的功能之一就是能夠定義可以在方法原來的代碼之前和之后被執(zhí)行的模板方法。
    相關文章
    Java基礎學習心得筆記

    Java基礎學習心得筆記

    對于很多只會C語言的初學者而言,面對java基礎語法學習,反而感覺很難,其實其中的問題不是語法難,而是一種編程思想的轉變。面向過程就是把你的代碼封裝成函數(shù),然后依次去做一件事情,面向過程是把你要做的事情抽象成對象,告訴對象去做。所以要想學好java入門,必須知道類和對象的概念。... [詳情]

    167人閱讀
    Python語言編程的特點及其應用

    Python語言編程的特點及其應用

    Python是一種計算機程序設計語言,相信了解Python的同學都知道它開發(fā)代碼的效率非常高,Python已經(jīng)成為最受歡迎的程序設計語言之一并且支持廣泛的應用開發(fā),今天和小編來一起看看Python!... [詳情]

    24431人閱讀
    讀完Java秘籍就是大神

    讀完Java秘籍就是大神

    Java是一種編程的語言,很多同學雖然會使用Java但是總是會有一些小問題,以下是小編給大家整理的Java的秘籍,一起來看看吧!... [詳情]

    23548人閱讀
    在Java中運用修復Bug的小技巧

    在Java中運用修復Bug的小技巧

    Java是一種編程語言,相信很多熟知Java的同學都會碰到一種常見的問題那就是在Java中運用動態(tài)掛載的過程中經(jīng)常會出現(xiàn)很多bug,那么針對性這些我們常見的bug我們應該如何解決呢?... [詳情]

    23837人閱讀
    如何零基礎學習UI設計

    如何零基礎學習UI設計

    隨著時代的發(fā)展目前學習主要有兩種方式,一種是自學,另一種是培訓班學習,當然學習UI設計也不例外:自學的話就要靠自己摸索學習,估計很多學員都沒有一個清晰的概念,要通過自學學好UI設計又談何容易呢?... [詳情]

    24605人閱讀
    零基礎學UI設計要多久?

    零基礎學UI設計要多久?

    近期有不少學員問過我,零基礎UI設計要多長時間.這次小編就和大家來看看零基礎學UI設計需要學習什么以及要多久時間吧!關于UI設計學習的時間這塊分了以下幾種類別.... [詳情]

    23127人閱讀