溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JAVA虛擬機JVM如何優(yōu)化

發(fā)布時間:2020-11-09 14:34:24 來源:億速云 閱讀:177 作者:小新 欄目:編程語言

小編給大家分享一下JAVA虛擬機JVM如何優(yōu)化,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

                                                           JAVA虛擬機JVM如何優(yōu)化

還以這個圖為例,從.java到.class是編譯過程,從.class到機器碼是解釋過程。下面對其進(jìn)行分別優(yōu)化。在優(yōu)化過程中,對編譯階段的優(yōu)化主要是對前端編譯器的優(yōu)化,在運行階段的優(yōu)化,主要是對即時編譯器的優(yōu)化。

JAVA虛擬機JVM如何優(yōu)化

編譯器優(yōu)化

編譯過程

JAVA虛擬機JVM如何優(yōu)化

以上為javac的編譯過程圖,以下為javac編譯過程的主體代碼。

JAVA虛擬機JVM如何優(yōu)化

下面對其步驟進(jìn)行詳細(xì)解讀
1、解析與填充符號表

詞法分析

將源代碼的字符流轉(zhuǎn)變?yōu)闃?biāo)記(Token)集合,標(biāo)記是編譯過程中的最小元素,如a,=,b,int。

語法分析

根據(jù)Token序列構(gòu)造抽象語法樹。以后編譯器基本不會再對源碼文件進(jìn)行操作了,后續(xù)的操作都是建立在抽象語法樹上。抽象語法樹是一種用來描述程序代碼語法結(jié)構(gòu)的樹形表示方式,節(jié)點代表代碼中的一個語法結(jié)構(gòu),例如修飾符,返回值等。

填充符號表

符號表是由一組符號地址和符號信息構(gòu)成的表格,用于編譯的不同階段。如在語義分析中,用于語義檢查和產(chǎn)生中間代碼;在目標(biāo)代碼生成階段,用于地址分配的依據(jù)。

2、注解處理器
這部分是插入式注解處理器在編譯期間對注解進(jìn)行處理的過程。其可以對語法樹進(jìn)行修改,一旦進(jìn)行了修改,編譯器將回到上面的第一步進(jìn)行重新處理,每一次循環(huán)稱為一個Round,也就是上圖中的回環(huán)過程。

3、語義分析與字節(jié)碼生成
在經(jīng)過語法分析后,生成的語法樹是一個結(jié)構(gòu)正確的源程序的抽象,但無法保證源程序是符合邏輯的。語義分析的任務(wù)是對結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查。比如下面代碼中的錯誤只能在語義分析階段檢查出來。

boolean a=false;
char b=2;
int c=a+b

此階段包括如下4個步驟:

標(biāo)注檢查

變量使用前是否已被聲明、變量與賦值之間的數(shù)據(jù)類型是否能夠匹配等。還有一個常量折疊,即把a=1+2變?yōu)閍=3。所以在代碼中的a=1+2和a=3并不會增加程序運行期cpu指令的運算量。

數(shù)據(jù)及控制流分析

檢查程序局部變量在使用前是否有賦值,方法的每條路徑是否都有返回值,是否所有的受查異常都被正確處理了等問題。在類加載時也有一個數(shù)據(jù)及控制流分析,其目的基本是一致的,但校驗的范圍不同,有些校驗項只有在編譯期或運行期才能運行。

解語法糖

語法糖是在計算機語言中添加某種語法,其對語言的功能沒有影響,但是能提高程序的可讀性。語法糖包括泛型,自動拆裝箱等。虛擬機運行時不支持這些語法,它們在編譯階段還原回基礎(chǔ)語法結(jié)構(gòu)。這個過程稱為解語法糖。

字節(jié)碼生成

將之前步驟生成的信息(語法樹、符號表)轉(zhuǎn)化成字節(jié)碼寫到磁盤中,然后添加和轉(zhuǎn)換了少量的代碼。如把字符串的加操作替換為StringBuffer或StringBuilder的append()操作。

至此,Class文件生成了。

語法糖

語法糖是java中添加某種語法,對語言的功能沒有影響,但是可以增加程序的可讀性。包括泛型、內(nèi)部類、枚舉類等。

1、泛型與類型擦除
泛型可用于類、接口和方法的創(chuàng)建中,用于對放入集合元素的類型的約束。泛型只在程序源碼中存在,在編譯階段有解語法糖的步驟,所以在.Class文件中,已經(jīng)變?yōu)榱嗽瓉淼脑愋土恕_@個過程叫做類型擦除。
泛型擦除前:

public static void main(String[] args){
    Map<String,String> map=new HashMap<>();
    map.put("姓名","小明");
    map.put("性別","男");
    sout(map.get("姓名"));
    sout(map.get("性別"));
}

泛型擦除后:

public static void main(String[] args){
    Map map=new HashMap();
    map.put("姓名","小明");
    map.put("性別","男");
    sout((String)map.get("姓名"));
    sout((String)map.get("性別"));
}

所以ArrayList和ArrayList在運行期時是同一個類。

2、自動拆裝箱、循環(huán)遍歷
這些是java中使用最多的語法糖。編譯前:

public static void main(String[] args){
      List<Integer> list=Arrays.asList(1,2,3,4);
      int sum=0;
      for(int i:list){
              sum +=i;
      }
      System.out.println(sum);
}

編譯后:

public static void main(String[] args){
      List list=Arrays.asList(new Integer[] {
                Integer.valueOf(1),
                Integer.valueOf(2),
                Integer.valueOf(3),
                Integer.valueOf(4)});
      int sum=0;
      for(Iterator localIterator=list.iterator();localIterator.hasNext();){
                   int i=((Integer)localIterator.next()).intValue();
                   sum +=i;
      }
      System.out.println(sum);
}

可見,自動拆裝箱在編譯后被轉(zhuǎn)化為了對應(yīng)的包裝和還原方法,如Integer.valueOf()和Integer.intValue()。
遍歷循環(huán)則把代碼還原為了迭代器的實現(xiàn)。

3、條件編譯
根據(jù)布爾常量值的真假,編譯器會把分支中不成立的代碼塊消除掉。

public static void main(String[] args){
      if(true){
            sout("block 1");
      }else{
           sout("block 2");
     }
}

編譯后,代碼變?yōu)椋?/p>

public static void main(String[] args){
     sout("block 1");
}

運行期優(yōu)化

一般情況下,我們將.java編譯成.class,.class再解釋成機器碼。但是也有特殊的情況。有些代碼調(diào)用比較頻繁,比如某個方法或代碼塊的運行特別頻繁,為了提高程序的執(zhí)行效率,在運行時,虛擬機會把這個代碼直接編譯成機器碼,并進(jìn)行各種層次的優(yōu)化。這樣的代碼稱為熱點代碼。完成這個任務(wù)的編譯器被稱為即時編譯器。但是其并不是虛擬機必需的部分。

JAVA虛擬機JVM如何優(yōu)化

即時編譯器的概述

(1)為什么虛擬機要使用解釋器和編譯器并存的架構(gòu)?

虛擬機里包含著解釋器和編譯器。當(dāng)程序需要迅速啟動和執(zhí)行的時候,解釋器可以首先發(fā)揮作用,省去編譯的時間,立即執(zhí)行。在程序運行后,隨著時間的推移,編譯器逐漸發(fā)揮作用,把越來越多的代碼編譯成本地代碼之后,可以獲取更高的執(zhí)行效率。

當(dāng)程序運行環(huán)境中內(nèi)存資源限制較大,可以使用解釋執(zhí)行節(jié)約內(nèi)存,反之可以使用編譯來提升效率。

(2)為什么虛擬機要實現(xiàn)兩個不同的即時編譯器?

虛擬機中內(nèi)置了兩個即時編譯器,分別為Client Compiler和Server Compiler,又稱為C1和C2。

默認(rèn)只使用其中的一個,至于選擇哪個,取決于虛擬機會根據(jù)自身版本和宿主機器的硬件性能自動選擇運行模式。用戶也可以使用“-client”、“-server”進(jìn)行指定。

(3)程序何時使用解釋器執(zhí)行?何時使用編譯器執(zhí)行?

虛擬機有一個分層編譯策略。

第0層:程序解釋執(zhí)行,解釋器不開啟性能監(jiān)控功能,可觸發(fā)第1層編譯

第1層:也稱為C1編譯,將字節(jié)碼編譯為本地代碼,進(jìn)行簡單、可靠的優(yōu)化,如有必要將加入性能監(jiān)控的邏輯。

第2層:也稱為C2編譯,將字節(jié)碼編譯為本地代碼,但是會啟用一些編譯耗時較長的優(yōu)化,甚至?xí)鶕?jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化。

(4)哪些程序代碼會被編譯為本地代碼?如何編譯為本地代碼?

熱點代碼包括如下兩類,其均把整個方法作為編譯對象。

a、被多次調(diào)用的方法

b、被多次執(zhí)行的循環(huán)體

熱點探測是用來判斷一段代碼是否為熱點代碼,其方式有兩種:

a、基于采樣

b、基于計數(shù)器。HotSpot使用的是這種。它為每個方法準(zhǔn)備了兩類計數(shù)器:統(tǒng)計方法被調(diào)用次數(shù)的方法調(diào)用計數(shù)器和統(tǒng)計一個方法中循環(huán)體代碼執(zhí)行次數(shù)的回邊計數(shù)器。

(5)如何從外部觀察及時編譯器的編譯過程和編譯結(jié)果?

可以使用 -xx:+PrintCompilation 查看哪些方法被即時編譯器編譯了。

優(yōu)化技術(shù)有哪些?

虛擬機的即時編譯器在生成代碼時,采用了如下的代碼優(yōu)化技術(shù)。

(1)公共子表達(dá)式消除

如果一個表達(dá)式E已經(jīng)計算過了,那如果再次出現(xiàn)E時就不會再對它進(jìn)行計算。比如:

int d=(a*b)*12+c+(c+b*a)

如果這段代碼交給javac編譯器,則不會進(jìn)行任何優(yōu)化。如果交給即時編譯器,會被進(jìn)行如下步驟的優(yōu)化:

第一步:消除公共子表達(dá)式

int d=E*12+c+(c+E)

第二步:代數(shù)化簡:

int d=E*13+c*2

(2)數(shù)組邊界檢查消除

數(shù)組邊界檢查是什么?
如果有一個數(shù)組foo[],在java語言中訪問數(shù)組元素foo[i]的時候,系統(tǒng)將會自動進(jìn)行上下界的范圍檢查,檢查i是否滿足0≤i≤foo.length這個條件。

那怎么進(jìn)行消除呢?
a、把運行期檢查提到編譯期完成。如foo[3],只要在編譯期根據(jù)數(shù)據(jù)流分析來確定foo.length的值,并判斷下標(biāo)“3”沒有越界,執(zhí)行的時候就不用判斷了。
b、隱式異常處理。這種思路通常用于空指針檢查和算符運算中除數(shù)為零的情況。

if(foo!=null){
  return foo.value;
}else{
  throw new NullPointException();
}

被隱式異常處理優(yōu)化后,變?yōu)槿缦麓a:

try{
  return foo.value;
}catch(segment_fault){
  uncommon_trap();
}

除了數(shù)組邊界檢查消除,還有自動裝箱消除、安全點消除、消除反射等。

(3)方法內(nèi)聯(lián)

把目標(biāo)方法的代碼“復(fù)制”到發(fā)起調(diào)用的方法之中,避免發(fā)生真實的方法調(diào)用。

public int add(int x1, int x2, int x3, int x4) {  
        return add1(x1, x2) + add1(x3, x4);  
 }  

public int add1(int x1, int x2) {  
        return x1 + x2;  
    }

運行一段時間后JVM會把add1方法去掉,并把代碼翻譯成:

public int add(int x1, int x2, int x3, int x4) {  
        return x1 + x2 + x3 + x4;  
}

(4)逃逸分析

當(dāng)一個對象在方法中被定義后,它可能被外部方法所引用,比如作為調(diào)用參數(shù)傳遞到其它方法中,這稱為方法逃逸。同理,如果被外部線程訪問到,它就稱為線程逃逸。

對變量進(jìn)行相應(yīng)分析就叫做逃逸分析。如果能證明別的方法或線程無法通過任何途徑訪問到這個對象,則可以為這個變量進(jìn)行一些優(yōu)化。

優(yōu)化的手段有棧上分配、同步消除、標(biāo)量替換等。以同步消除為例,如果逃逸分析能夠確定一個變量不會逃逸出線程,即無法被其它線程訪問到,那對這個變量實施的同步措施就可以消除掉了。

以上是JAVA虛擬機JVM如何優(yōu)化的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI