逃逸分析
在計(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)]明出處。