天天躁日日躁狠狠躁白人_日本aa级毛片免费观看_一级黄色短片_日韩视频中文字幕_国产一区二区精品在线观看_婷婷久久综合

修改機器碼的重要性—修改機器碼軟件好使嗎

首頁 > 汽車 > 汽車資訊 > 正文

修改機器碼的重要性—修改機器碼軟件好使嗎

JIT(Just In Time)技術是Java虛擬機中的一項重要技術,其在運行時將字節碼編譯為機器碼,以大幅提升程序的執行速度。

正是因為JVM中使用了JIT技術,才為Java代碼在運行時的性能可能超過C++提供了基礎。

一般情況下,我們所產出的代碼,很大層面上需要保障代碼的可讀性,而這里的可讀性是針對于編碼人員的,而非針對于機器。具備高可讀性的代碼,通常并不意味著其可以高效地被機器直接執行,而通常情況下剛好相反。

此處,我們針對JIT中一些常用的優化手段,來理解為何Java代碼的執行效率可以如此之高。

經過JIT優化的代碼的執行效率提升,很大層面上是因為JIT對指令進行了重新的排列。指令重排在保證代碼邏輯不變的情況下,對代碼的執行順序進行了調整,從而提升了代碼的執行效率。

為了理解指令重排,我們需要首先了解JVM所支持的指令是什么樣子的。

對于已經編譯完成的一個方法,存在三個重要的組成部分:

  • 本地變量表:用以保存方法的入參及聲明的局部變量。
  • 操作數棧:用以存儲運行的中間結果。
  • 指令集:即編譯完成后的代碼,也可能稱為字節碼。

Java指令在JVM規范中有詳細的描述,對應版本的JVM都會擁有一份JVM規范的文檔,這些文檔被收錄在Oracle的官網中:

在對應文檔的“The Java Machine Instruction Set”章節中,有對各種執行的詳細介紹,此處我們不過多贅述,而是簡單討論一下指令的行為。

JVM所支持的指令,從行為中可分為四類:

  • 從本地變量表或常量池中取出一個值,并將其壓入到操作數棧中。如aload_0,將本地變量表中索引為0的值壓入到操作數棧中。
  • 從操作數棧中取出操作數進行計算,并將操作結果重新壓入到操作數棧中。如iadd,從操作數棧中取出兩個32位整數,并將其相加得到的和重新壓入到操作數棧中。
  • 從操作數棧中取出值并寫入到本地變量表中。如astore_0,從操作數棧中取出一個值,并將其寫入到本地變量表中索引為0的位置。
  • 用于控制程序跳轉。如if_icmpeq、lookupswitch、tableswitch等。

此處我們著重了解前三類指令,首先看示例代碼:

我們將這段代碼編譯成為class文件,并通過javap命令查看編譯后的結果。

輸出的結果如下:

這里我們只關注最后public int compute(int, int)方法中的指令:

我們可以看到,在源代碼中的兩行代碼,編譯完成后得到了8條指令,這8條指令是完全按照源代碼的意圖進行直譯的。

而在實際執行中,JVM會對指令進行簡化,簡化后的指令:

我們可以看到,指令從8條被精簡到了6條,其中針對操作數棧頂的值的讀取和寫入(即istore_3和iload_3)被合并,從而減少了不必要的操作。

那么此時,我們就可以理解指令重排的意義。

在編碼過程中,從提高代碼可讀性的角度考慮,我們會將含義、目的將近的變量放到一起聲明和初始化,并在后續操作中,按更容易理解的業務語義來對其進行批量操作,但是這個時候,可能會導致很多無效的讀取和寫入操作。為了合并掉這些操作,JVM在邏輯不變的前提下,對指令進行重排,從而使得更多的指令被合并,減少同一代碼執行時所需的指令數量。

而指令重排所帶來的好處是顯而易見的,如果指令的數量被降低10%,那么性能將是實打實地提升10%。

逃逸分析是在Java6中引入的新特性,其與標量替換共同完成運行時的優化。

逃逸分析用來判斷在一個方法中所實例化的對象,是否在方法外被使用。如果對象在方法外被使用,則表示這個對象發生了“**逃逸**”,否則視作未發生“**逃逸**”。而對于未發生逃逸的對象,則可通過棧上分配技術,直接在方法棧中為對象分配內存。進而通過**標量替換**技術,將變量中的字段打散到方法的本地變量表中,后續對于對象中字段的操作,就直接操作這些本地變量,此時這個對象就不見了,取而代之的是表示其所包含字段的本地變量。

標量是不可再被細分的值,如32位整數、布爾值、字符串等。標量不僅局限于基本數據類型。

此優化所帶來的好處有:

  • 因為不再需要實例化對象,因此減少了堆內存的使用,降低了垃圾回收的壓力,更多的內存可隨著方法棧的銷毀而直接被釋放。
  • 鎖消除,因為對象不會發生逃逸,因此對象的作用域僅在方法執行過程中,因此其是不會發生線程同步的。此時無效的對于同步鎖的操作將被消除掉,提升執行效率。
  • 替換為標量的值,在方法邏輯執行過程中,可以參與到指令重排中,從而進一步優化性能。

因此,對于以下代碼:

在進行標量替換后,其實際的邏輯將近似地被優化為:

以上僅是一個示意,當然,此間還涉及到一些其他的優化手段,比如內聯等。

內聯的概念比較容易理解,即是將一個方法的邏輯直接打平打調用方的代碼中。例如:

在內聯后即成為:

內聯的好處有很多,例如降低代碼的實際調用層次等。但是相比于其直接產生的收益,其間接收益則更大。內聯是將各種優化手段有效銜接起來的重要手段。例如,逃逸分析的重要依據是對象是否在方法外被使用,如果我們將一個對象傳入到一個方法中,例如對其字段進行校驗等,那么這個對象就發生了逃逸,不能應用棧上分配、標量替換等優化手段,更進一步也就無法更好地進行指令重排。

而內聯則有效地解決了這個問題,在實際代碼運行過程中,內聯無處不在,通常幾層、十幾層的調用棧,都會被內聯到一個方法中。

那么,什么樣的方法可以被內聯呢?

簡單來說,穩定的方法可以被內聯。即當一個方法調用另一個方法時,如果另一個方法的邏輯不會發生變化,那么這個方法就可以被內聯到調用方的方法中。例如final方法、private方法等。

但是因為內聯的優化手段實在過于重要,因此JVM后期對內聯再次進行了增強,也就是所說的激進優化。這里的激進優化主要在于可能發生變化的方法,如通過接口調用一個實現時。

一般情況下,當我們通過接口調用一個方法時,我們并不能確定最終調用的是接口的哪個實現。當對這個接口方法的調用成為熱點,且目標方法不曾發生改變時,將嘗試對這個被調用的方法進行內聯,如果后續調用的目標方法發生變化,則會進行優化回退。優化回退的成本相對是很高的,因為一般情況下,代碼將被回退到所有優化發生之前的狀態。

這里所說的激進優化,與JVM參數中的AggressiveOpts是不同的,AggressiveOpts參數的開啟表示將啟用當前JVM版本中還不成熟的優化手段。

那么,激進優化的意義何在和?

一般情況下,在編碼過程中,需要考慮到諸如并行開發、接口分離原則等諸多方面,會使我們的代碼在設計層面被拆分成為不同的組件,而大多情況下,這些用作分離的接口通常只有一個實現(默認實現),這就為激進優化帶來的底層的邏輯支撐。

JVM中還存在諸多的優化手段,如分支消除、反射優化等。但是總體而言,**JIT的優化主要依據在于熱點代碼判斷,最重要的手段在于方法內聯**。因此當我們進行代碼設計時,首要應考慮代碼的內聯屬性。如果組件的代碼是更容易被內聯的,通常情況下,其所帶來的效率將會更高?;诖?,可以總結一些有效的代碼設計方法:

  • 多抽取工具方法。工具方法的抽取除了代碼更好的可靠性外,也更便于內聯的發生,并不會帶來額外的調用棧開銷。
  • 明確擴展點。在進行類設計時,對于哪些方法是需要多態的應該有明確的規劃,對于不需要多態的方法應明確使用final進行封閉。
  • 單純的沒有多態的接口分離是不會帶來額外的性能損耗的,因為這些方法最終會被內聯掉。

備案號:贛ICP備2022005379號
華網(http://www.b3q24.cn) 版權所有未經同意不得復制或鏡像

QQ:51985809郵箱:51985809@qq.com

主站蜘蛛池模板: 国产一级片免费在线观看 | 青在线视频 | 久久夜色精品国产www红杏 | 蜜臀av日日欢夜夜爽一区 | 亚洲毛片在线看 | 海角真实乱视频 | 精品一区二区三区免费毛片爱 | 国产99久久| 久久久久久久久久久久久久久久久久久 | 欧美综合色网 | 亚洲一区二区三区免费在线观看 | 久一精品| 日韩小视频在线播放 | 337p日本欧洲亚洲鲁鲁 | 亚洲精品成人av | 亚洲一二三区视频 | 亚洲一级毛片免费看 | 在线观看国产欧美 | 成人免费一区二区三区在线观看 | 国产剧情在线一区 | 麻豆电影免费观看 | 欧美 日韩 国产 成人 在线 | 日韩激情电影一区二区 | 五月综合激情 | 九九九久久久精品 | www.色女人| 国产一区二区观看 | 亚洲午夜久久久久久久久红桃 | 91精品久久久久久久99软件 | 免费网站日本a级淫片免费看 | 日日操天天 | 亚洲一区二区三区高清视频 | www.com色| 嘿咻嘿咻成人免费视频播放 | 黄页免费观看 | 久久久精品国产一区二区电影四季 | 日本亚洲欧洲无免费码在线 | 欧美色图在线观看 | 欧美激情图区 | 日韩欧美精品中文字幕 | 国产一级特黄真人毛片 |