編譯器中的 逃逸分析

逃逸分析

在計(jì)算機(jī)語言編譯器語言優(yōu)化管理中,分析指針動態(tài)范圍的方法稱之為逃逸分析。

通俗點(diǎn)講,當(dāng)一個(gè)對象的指針被多個(gè)方法或線程引用時(shí),我們稱這個(gè)指針發(fā)生了逃逸。

public class G {
    public static B b;
    
    public void globalVariablePointerEscape(){//給全局變量賦值,發(fā)生逃逸
        b=new B();
    }
    
    public B methodPointerEscape(){//方法返回值,發(fā)生逃逸
        return new B();
    }
    
    public void instancePassPointerEscape(){
        methodPointerEscape().printClassName(this);//實(shí)例引用發(fā)生逃逸
    }
    
    
}

class B{
    public void printClassName(G g){
        System.out.println(g.getClass().getName());
    }
}

在這個(gè)例子中,一共舉了3種常見的指針逃逸場景。分別是 全局變量賦值,方法返回值,實(shí)例引用傳遞。

逃逸分析優(yōu)化JVM原理

我們知道java對象是在堆里分配的,在調(diào)用棧中,只保存了對象的指針。

當(dāng)對象不再使用后,需要依靠GC來遍歷引用樹并回收內(nèi)存,如果對象數(shù)量較多,將給GC帶來較大壓力,也間接影響了應(yīng)用的性能。減少臨時(shí)對象在堆內(nèi)分配的數(shù)量,無疑是最有效的優(yōu)化方法。

怎么減少臨時(shí)對象在堆內(nèi)的分配數(shù)量呢?不可能不實(shí)例化對象吧!

場景介紹

其實(shí),在java應(yīng)用里普遍存在一種場景。一般是在方法體內(nèi),聲明了一個(gè)局部變量,且該變量在方法執(zhí)行生命周期內(nèi)未發(fā)生逃逸(在方法體內(nèi),未將引用暴露給外面)。
按照J(rèn)VM內(nèi)存分配機(jī)制,首先會在堆里創(chuàng)建變量類的實(shí)例,然后將返回的對象指針壓入調(diào)用棧,繼續(xù)執(zhí)行。

這是優(yōu)化前,JVM的處理方式。

逃逸分析優(yōu)化 - 棧上分配

優(yōu)化原理:分析找到未逃逸的變量,將變量類的實(shí)例化內(nèi)存直接在棧里分配(無需進(jìn)入堆),分配完成后,繼續(xù)在調(diào)用棧內(nèi)執(zhí)行,最后線程結(jié)束,棧空間被回收,局部變量對象也被回收。

這是優(yōu)化后的處理方式,對比可以看出,主要區(qū)別在棧空間直接作為臨時(shí)對象的存儲介質(zhì)。從而減少了臨時(shí)對象在堆內(nèi)的分配數(shù)量。

逃逸分析的原理很簡單,但JVM在應(yīng)用過程中,還是有諸多考慮。

比如,逃逸分析不能在靜態(tài)編譯時(shí)進(jìn)行,必須在JIT里完成。原因是,與java的動態(tài)性有沖突。因?yàn)槟憧梢栽谶\(yùn)行時(shí),通過動態(tài)代理改變一個(gè)類的行為,此時(shí),逃逸分析是無法得知類已經(jīng)變化了。

逃逸分析并不是直接的優(yōu)化手段,而是一個(gè)代碼分析,通過動態(tài)分析對象的作用域,為其它優(yōu)化手段如棧上分配、標(biāo)量替換和同步消除等提供依據(jù),發(fā)生逃逸行為的情況有兩種:方法逃逸和線程逃逸。

1、方法逃逸:當(dāng)一個(gè)對象在方法中定義之后,作為參數(shù)傳遞到其它方法中;
2、線程逃逸:如類變量或?qū)嵗兞?,可能被其它線程訪問到;

如果不存在逃逸行為,則可以對該對象進(jìn)行如下優(yōu)化:同步消除、標(biāo)量替換和棧上分配。

同步消除

線程同步本身比較耗,如果確定一個(gè)對象不會逃逸出線程,無法被其它線程訪問到,那該對象的讀寫就不會存在競爭,則可以消除對該對象的同步鎖,通過-XX:+EliminateLocks可以開啟同步消除。

標(biāo)量替換

1、標(biāo)量是指不可分割的量,如java中基本數(shù)據(jù)類型和reference類型,相對的一個(gè)數(shù)據(jù)可以繼續(xù)分解,稱為聚合量;
2、如果把一個(gè)對象拆散,將其成員變量恢復(fù)到基本類型來訪問就叫做標(biāo)量替換;
3、如果逃逸分析發(fā)現(xiàn)一個(gè)對象不會被外部訪問,并且該對象可以被拆散,那么經(jīng)過優(yōu)化之后,并不直接生成該對象,而是在棧上創(chuàng)建若干個(gè)成員變量;
通過-XX:+EliminateAllocations可以開啟標(biāo)量替換, -XX:+PrintEliminateAllocations查看標(biāo)量替換情況。

棧上分配

故名思議就是在棧上分配對象,其實(shí)目前Hotspot并沒有實(shí)現(xiàn)真正意義上的棧上分配,實(shí)際上是標(biāo)量替換。

作者:占小狼
鏈接:http://www.itdecent.cn/p/20bd2e9b1f03
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)繫作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請?jiān)]明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容