真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JVM編譯器的介紹和使用-創(chuàng)新互聯(lián)

Java編譯器在JVM性能優(yōu)化系列的第二篇文章中占據(jù)中心位置。 Eva Andreasson介紹了不同種類的編譯器,并比較了客戶端,服務器和分層編譯的性能結果。最后,她概述了常見的JVM優(yōu)化,例如消除死代碼,內聯(lián)和循環(huán)優(yōu)化。

10年積累的成都網(wǎng)站建設、網(wǎng)站設計經驗,可以快速應對客戶對網(wǎng)站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡服務。我雖然不認識你,你也不認識我。但先網(wǎng)站制作后付款的網(wǎng)站建設流程,更有華鎣免費網(wǎng)站建設讓你可以放心的選擇與我們合作。

Java編譯器是Java著名的平臺的獨立性的來源。軟件開發(fā)人員會盡力編寫最好的Java應用程序,然后編譯器會在幕后進行工作,以為目標目標平臺生成高效且性能良好的執(zhí)行代碼。不同種類的編譯器可滿足各種應用程序需求,從而產生特定的所需性能結果。你對編譯器了解得越多,就它們的工作方式和可用的種類而言,你就越能夠優(yōu)化Java應用程序性能。

什么是編譯器?

簡而言之,編譯器將編程語言作為輸入,并產生可執(zhí)行語言作為輸出。 一種常見的編譯器是Javac,它包含在所有標準Java開發(fā)工具包(JDK)中。 javac將Java代碼作為輸入并將其轉換為字節(jié)碼-JVM的可執(zhí)行語言。 字節(jié)碼存儲在.class文件中,當啟動Java進程時,該文件將加載到Java運行時中。

字節(jié)碼不能被標準CPU讀取,需要轉換為底層執(zhí)行平臺可以理解的指令語言。 JVM中負責將字節(jié)碼轉換為可執(zhí)行平臺指令的組件是另一個編譯器。 一些JVM編譯器可以處理多個級別的轉換。 例如,編譯器可能在將字節(jié)碼轉換成實際的機器指令(即翻譯的最后一步)之前,創(chuàng)建字節(jié)碼的各種中間表示形式。

從平臺不可知的角度來看,我們希望盡可能使代碼獨立于平臺,以便最后的翻譯級別(從最低表示到實際的機器代碼)是將執(zhí)行鎖定到特定平臺的處理器體系結構的步驟 。 靜態(tài)和動態(tài)編譯器之間的最高級別隔離。 從那里開始,我們有選擇,這取決于我們要針對的執(zhí)行環(huán)境,所需的性能結果以及需要滿足的資源限制。 我在本系列的第1部分中簡要討論了靜態(tài)和動態(tài)編譯器。 在以下各節(jié)中,我將進一步解釋。

靜態(tài)與動態(tài)編譯

靜態(tài)編譯器的一個示例是前面提到的javac。 對于靜態(tài)編譯器,輸入代碼將被解釋一次,而輸出可執(zhí)行文件的形式將在執(zhí)行程序時使用。 除非你對原始源代碼進行更改并重新編譯代碼(使用編譯器),否則輸出將始終產生相同的結果。 這是因為輸入是靜態(tài)輸入,而編譯器是靜態(tài)編譯器。

在靜態(tài)編譯中,

static int add7( int x ) {
    return x+7;}

會導致類似于以下字節(jié)碼的內容:

iload0
 bipush 7
 iadd
 ireturn

動態(tài)編譯器會動態(tài)地將一種語言翻譯成另一種語言,這意味著它會在執(zhí)行代碼時發(fā)生-在運行時! 動態(tài)編譯和優(yōu)化為運行時提供了能夠適應應用程序負載變化的優(yōu)勢。 動態(tài)編譯器非常適合Java運行時,這些運行時通常在不可預測且不斷變化的環(huán)境中執(zhí)行。 大多數(shù)JVM使用動態(tài)編譯器,例如即時(JIT)編譯器。 問題是動態(tài)編譯器和代碼優(yōu)化有時需要額外的數(shù)據(jù)結構,線程和CPU資源。 優(yōu)化或字節(jié)碼上下文分析越高級,編譯消耗的資源就越多。 與輸出代碼的顯著性能相比,在大多數(shù)環(huán)境中,開銷仍然很小。

JVM種類和Java平臺的獨立性

所有JVM實現(xiàn)都有一個共同點,那就是它們試圖將應用程序字節(jié)碼轉換為機器指令。 一些JVM在加載時解釋應用程序代碼,并使用性能計數(shù)器來關注“熱”代碼。 一些JVM跳過解釋,僅依靠編譯。 編譯的資源密集性可能會受到更大的影響(尤其是對于客戶端應用程序),但它還可以實現(xiàn)更高級的優(yōu)化。

從Java字節(jié)碼到執(zhí)行

將Java代碼編譯為字節(jié)碼后,下一步就是將字節(jié)碼指令轉換為機器碼。 這可以由解釋器或編譯器完成。

解釋

字節(jié)碼編譯的最簡單形式稱為解釋。 解釋器只需為每個字節(jié)碼指令查找硬件指令,然后將其發(fā)送出去以由CPU執(zhí)行。

你可能會想到解釋,類似于使用字典:對于特定單詞(字節(jié)碼指令),存在確切的翻譯(機器碼指令)。 由于解釋器一次讀取并立即執(zhí)行一個字節(jié)碼指令,因此沒有機會對指令集進行優(yōu)化。 每次調用字節(jié)碼時,解釋器也必須執(zhí)行解釋,這使它相當慢。 解釋是執(zhí)行代碼的一種準確方法,但是未優(yōu)化的輸出指令集可能不是目標平臺處理器的最高性能序列。

總結

另一方面,編譯器會將要執(zhí)行的整個代碼加載到運行時中。在翻譯字節(jié)碼時,它可以查看整個或部分運行時上下文,并就如何實際翻譯代碼做出決策。它的決策基于對代碼圖的分析,例如對指令和運行時上下文數(shù)據(jù)的不同執(zhí)行分支。

當將字節(jié)碼序列轉換為機器碼指令集并可以對該指令集進行優(yōu)化時,替換指令集(例如優(yōu)化序列)將存儲到稱為代碼緩存的結構中。下次執(zhí)行該字節(jié)碼時,先前優(yōu)化的代碼可以立即位于代碼緩存中,并用于執(zhí)行。在某些情況下,性能計數(shù)器可能會加入并覆蓋之前的優(yōu)化,在這種情況下,編譯器將運行新的優(yōu)化序列。代碼緩存的優(yōu)點是可以立即執(zhí)行生成的指令集-無需解釋性查找或編譯!這加快了執(zhí)行時間,尤其是對于Java方法,其中多次調用相同的方法。

優(yōu)化

除了動態(tài)編譯外,還可以插入性能計數(shù)器。 例如,編譯器可能會插入一個性能計數(shù)器,以在每次調用字節(jié)碼塊(例如對應于特定方法)時進行計數(shù)。 編譯器使用有關給定字節(jié)碼有多“熱”的數(shù)據(jù)來確定代碼中的最優(yōu)化將對運行中的應用程序產生最佳影響。 運行時概要分析數(shù)據(jù)使編譯器可以即時制定豐富的代碼優(yōu)化決策集,從而進一步提高代碼執(zhí)行性能。 隨著更完善的代碼概要分析數(shù)據(jù)的可用,它可以用于做出其他更好的優(yōu)化決策,例如:如何更好地以編譯為語言對指令進行排序,是否用更高效的指令集代替指令集,甚至 是否消除冗余操作。


考慮一下Java代碼:

static int add7( int x ) {
    return x+7;}

這可以由javac靜態(tài)編譯為字節(jié)碼:

iload0
 bipush 7
 iadd
 ireturn

調用該方法時,字節(jié)碼塊將動態(tài)編譯為機器指令。 當性能計數(shù)器(如果存在于代碼塊中)達到閾值時,它可能也會得到優(yōu)化。 對于給定的執(zhí)行平臺,最終結果可能類似于以下機器指令集:

lea rax,[rdx+7]
ret

不同應用程序的不同編譯器

不同的Java應用程序有不同的需求。 長期運行的企業(yè)服務器端應用程序可以進行更多優(yōu)化,而較小的客戶端應用程序可能需要以最小的資源消耗來快速執(zhí)行。 讓我們考慮三種不同的編譯器設置及其各自的優(yōu)缺點。

客戶端編譯器
著名的優(yōu)化編譯器是C1,它是通過-client JVM啟動選項啟用的編譯器。 顧名思義,C1是客戶端編譯器。 它是為客戶端應用程序設計的,這些客戶端應用程序具有較少的可用資源,并且在許多情況下對應用程序啟動時間敏感。 C1使用性能計數(shù)器進行代碼性能分析,以實現(xiàn)簡單,相對無干擾的優(yōu)化。

服務器端編譯器
對于長時間運行的應用程序(例如服務器端企業(yè)Java應用程序),客戶端編譯器可能不夠。 可以使用類似C2的服務器端編譯器。 通常通過在啟動命令行中添加JVM啟動選項-server來啟用C2。 由于大多數(shù)服務器端程序預計將運行很長時間,因此啟用C2意味著你將比使用運行時間短的輕量級客戶端應用程序收集更多的性能分析數(shù)據(jù)。 因此,你將能夠應用更高級的優(yōu)化技術和算法。

服務器編譯器比客戶端編譯器處理更多的概要分析數(shù)據(jù),并且允許進行更復雜的分支分析,這意味著它將考慮哪種優(yōu)化路徑會更有利。具有更多可用的概要分析數(shù)據(jù)可產生更好的應用程序結果。當然,進行更廣泛的分析和分析需要在編譯器上花費更多的資源。啟用了C2的JVM將使用更多的線程和更多的CPU周期,需要更大的代碼緩存,依此類推。

分層編譯
分層編譯將客戶端和服務器端編譯組合在一起。 Azul首先在其Zing JVM中提供了分層編譯。最近(從Java SE 7開始),Oracle Java Hotspot JVM已采用它。分層編譯利用了JVM中客戶端和服務器編譯器的優(yōu)勢。客戶端編譯器在應用程序啟動期間最活躍,并處理由較低的性能計數(shù)器閾值觸發(fā)的優(yōu)化??蛻舳司幾g器還會插入性能計數(shù)器并為更高級的優(yōu)化準備指令集,服務器端編譯器將在稍后階段解決這些問題。分層編譯是一種非常節(jié)省資源的性能分析方法,因為編譯器能夠在影響較小的編譯器活動期間收集數(shù)據(jù),以后可以將其用于更高級的優(yōu)化。與僅使用解釋的代碼配置文件計數(shù)器所獲得的信息相比,這種方法還可以產生更多的信息。

圖1中的圖表架構描述了純解釋,客戶端,服務器端和分層編譯之間的性能差異。 X軸顯示執(zhí)行時間(時間單位),Y軸性能(操作數(shù)/時間單位)。

與純解釋代碼相比,使用客戶端編譯器可以使執(zhí)行性能(以ops / s為單位)提高約5至10倍,從而提高了應用程序性能。增益的變化當然取決于編譯器的效率,啟用或實現(xiàn)的優(yōu)化方式以及(在較小程度上)應用程序相對于目標執(zhí)行平臺的良好設計。不過,后者確實是Java開發(fā)人員永遠不必擔心的事情。

與客戶端編譯器相比,服務器端編譯器通??蓪⒋a性能提高30%到50%。在大多數(shù)情況下,性能改進將平衡額外的資源成本。

分層編譯結合了兩種編譯器的最佳功能??蛻舳司幾g可縮短啟動時間并加快優(yōu)化速度,而服務器端編譯可在執(zhí)行周期后期提供更高級的優(yōu)化。
一些常見的編譯器優(yōu)化

消除無效代碼
消除無效代碼聽起來像是:消除從未被調用的代碼-即 無效 代碼。 如果編譯器在運行時發(fā)現(xiàn)某些指令是不必要的,它將僅從執(zhí)行指令集中消除它們。 例如,在清單1中,從不使用變量的特定值分配,并且可以在執(zhí)行時將其完全忽略。 在字節(jié)碼級別,這可能對應于永遠不需要執(zhí)行將值加載到寄存器中的操作。 不必執(zhí)行加載意味著更少的CPU時間,從而縮短了代碼執(zhí)行速度,進而縮短了應用程序的時間-尤其是當代碼很熱并且每秒被調用幾次時。

清單1顯示了Java代碼,該代碼示例了一個從未使用過的變量,這是不必要的操作。
清單 1. 清除無效代碼

int timeToScaleMyApp(boolean endlessOfResources) {
  int reArchitect = 24;
  int patchByClustering = 15;
  int useZing = 2;

  if(endlessOfResources)
    return reArchitect + useZing;
  else
    return useZing;}

在字節(jié)碼級別上,如果加載了一個值但從未使用過,則編譯器可以檢測到該值并消除死代碼,如清單2所示。從不執(zhí)行加載可以節(jié)省CPU時間,從而提高程序的執(zhí)行速度。

清單2.優(yōu)化后的相同代碼

int timeToScaleMyApp(boolean endlessOfResources) {
  int reArchitect = 24;
  //unnecessary operation removed here...
  int useZing = 2;

  if(endlessOfResources)
    return reArchitect + useZing;
  else
    return useZing;}

冗余消除是類似的優(yōu)化,它刪除重復的指令以提高應用程序性能。

內聯(lián)
許多優(yōu)化嘗試消除機器級別的跳轉指令(例如,用于x86架構的JMP)。 跳轉指令更改指令指針寄存器,從而傳輸執(zhí)行流程。 相對于其他ASSEMBLY指令,這是一項昂貴的操作,這就是為什么它是減少或消除的常見目標的原因。 針對此的非常有用且眾所周知的優(yōu)化稱為內聯(lián)。 由于跳轉很昂貴,因此將許多頻繁調用具有不同入口地址的小型方法的調用內插會很有幫助。 清單3至5中的Java代碼體現(xiàn)了內聯(lián)的好處。

清單3.調用者方法

int whenToEvaluateZing(int y) {
  return daysLeft(y) + daysLeft(0) + daysLeft(y+1);}

清單4.調用的方法

int daysLeft(int x){
  if (x == 0)
    return 0;
  else
    return x - 1;}

清單5.內聯(lián)方法

int whenToEvaluateZing(int y){
  int temp = 0;

  if(y == 0) temp += 0; else temp += y - 1;
  if(0 == 0) temp += 0; else temp += 0 - 1;
  if(y+1 == 0) temp += 0; else temp += (y + 1) - 1;

  return temp; }

在清單3至清單5中,調用方法對一個小方法進行了3次調用,出于示例的考慮,我們認為對內聯(lián)方法而言,跳轉到三遍更為有益。

內聯(lián)很少調用的方法可能不會有太大的區(qū)別,但是內聯(lián)經常調用的所謂“熱”方法可能意味著性能上的巨大差異。 內聯(lián)還經常為進一步的優(yōu)化讓路,如清單6所示
清單6.內聯(lián)之后,可以應用更多的優(yōu)化

int whenToEvaluateZing(int y){
  if(y == 0) return y;
  else if (y == -1) return y - 1;
  else return y + y - 1;}

循環(huán)優(yōu)化
循環(huán)優(yōu)化在減少執(zhí)行循環(huán)帶來的開銷方面發(fā)揮著重要作用。 在這種情況下,開銷意味著昂貴的跳轉,條件檢查的次數(shù),非最佳指令流水線(即導致CPU不工作或額外循環(huán)的指令順序)。 循環(huán)優(yōu)化有很多種,總計有很多優(yōu)化。 值得注意的包括:

合并循環(huán):當兩個附近的循環(huán)重復相同的時間時,如果主體中沒有相互引用的情況,則編譯器可以嘗試合并循環(huán)的主體,以同時(并行)執(zhí)行,即它們彼此完全獨立。
反轉循環(huán):基本上,你將常規(guī)的while循環(huán)替換為do-while循環(huán)。并且do-while循環(huán)設置為if子句。這種替換導致更少的兩次跳躍。但是,它增加了條件檢查,因此增加了代碼大小。此優(yōu)化是一個很好的示例,說明了如何使用更多的資源來提高代碼效率-編譯器必須在運行時動態(tài)評估和決定成本與收益的平衡。
切片循環(huán):重新組織循環(huán),以便對大小適合緩存的數(shù)據(jù)塊進行迭代。
展開循環(huán):減少必須評估循環(huán)條件的次數(shù)以及跳轉次數(shù)。你可以將其視為“內聯(lián)”要執(zhí)行的主體的多次迭代,而不會越過循環(huán)條件。展開循環(huán)會帶來風險,因為展開循環(huán)可能會損害流水線并導致多次冗余指令提取,從而降低性能。再次,這是編譯器在運行時做出的判斷調用,即,如果增益足夠,則成本可能是值得的。

另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。


網(wǎng)站欄目:JVM編譯器的介紹和使用-創(chuàng)新互聯(lián)
文章URL:http://weahome.cn/article/dgdchj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部