類加載機(jī)制
類從加載導(dǎo)卸載出內(nèi)存的整個(gè)生命周期如上圖所示策菜。圖中的7個(gè)階段中晶疼,加載、驗(yàn)證又憨、準(zhǔn)備翠霍、初始化和卸載的順序是確定的,而解析和使用階段不一定蠢莺,解析可能在初始化之后(動(dòng)態(tài)綁定)寒匙。
類加載時(shí)機(jī)
有且只有以下5種情況:
- 遇到new、getstatic躏将、putstatic锄弱、invokestatic等字節(jié)碼,對(duì)應(yīng)Java代碼中的new對(duì)象祸憋、讀取或者設(shè)置類的靜態(tài)變量会宪、調(diào)用類的靜態(tài)方法;
- 使用reflect包進(jìn)行反射的時(shí)候蚯窥;
- 初始化類時(shí)掸鹅,若父類未初始化,則先出發(fā)父類的初始化拦赠;
- JVM啟動(dòng)時(shí)巍沙,執(zhí)行的主類(包含main()方法的類);
- JDK1.7以后動(dòng)態(tài)語(yǔ)言支持矛紫。
注意是有且僅有赎瞎,其他情況,譬如數(shù)組定義引用到未加載的類颊咬、調(diào)用類的靜態(tài)常量(存儲(chǔ)在常量池中)等其他情況并不會(huì)觸發(fā)類的初始化加載务甥。
加載
完成以下事情:
- 通過(guò)類的全限定名獲取類的二進(jìn)制字節(jié)流(不一定從文件獲取,也可能是從網(wǎng)絡(luò)喳篇、zip包敞临、動(dòng)態(tài)代理、其他文件如jsp等途徑生成)麸澜;
- 將字節(jié)流的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)挺尿,具體由虛擬機(jī)自行實(shí)現(xiàn)定義;
- 生成對(duì)應(yīng)java.lang.Class對(duì)象,放在方法區(qū)编矾。
數(shù)組類本身不通過(guò)類加載器創(chuàng)建熟史,而是JVM直接創(chuàng)建的,如果數(shù)組類加載的時(shí)候窄俏,其組件類型(去掉最外面維度之后的類型)是引用類型蹂匹,則遞歸地加載。
加載階段和連接階段的部分內(nèi)容(比如一部分字節(jié)碼文件格式的驗(yàn)證動(dòng)作)時(shí)交叉進(jìn)行的凹蜈,但兩者的開(kāi)始時(shí)間肯定是保持先后順序的限寞。
驗(yàn)證
驗(yàn)證步驟是為了確保Class文件的字節(jié)流中的信息符合JVM要求,且不危害JVM安全仰坦。驗(yàn)證過(guò)程又細(xì)分為以下4個(gè)子過(guò)程:
- 文件格式驗(yàn)證:驗(yàn)證是否符合Class文件格式履植,如果驗(yàn)證到不符合Class文件格式約束,則JVM拋出java.lang.VerifyError異城幕危或其子類玫霎;
- 元數(shù)據(jù)驗(yàn)證:進(jìn)行語(yǔ)義分析,保證其符合Java語(yǔ)言規(guī)范传泊;
- 字節(jié)碼驗(yàn)證:通過(guò)數(shù)據(jù)流和控制流分析鼠渺,確認(rèn)語(yǔ)義合法且符合邏輯。JDK1.6之后的javac在Code屬性的屬性表里面增加了一項(xiàng)“StackMapTable”屬性眷细,描述了方法提中所有基本塊(按控制流拆分的代碼塊)開(kāi)始時(shí)本地變量表和操作棧應(yīng)有的狀態(tài)拦盹,字節(jié)碼驗(yàn)證過(guò)程中秩序檢查StackMapTable屬性的記錄是否合法即可;
- 符號(hào)引用驗(yàn)證:在連接的第三階段——解析階段中溪椎,JVM將符號(hào)引用轉(zhuǎn)化為直接引用進(jìn)行符號(hào)引用驗(yàn)證普舆,對(duì)類自身以外的信息進(jìn)行匹配行校驗(yàn),比如符號(hào)引用的類是否能找到校读,類沼侣、方法、字段的訪問(wèn)性是否能被當(dāng)前類訪問(wèn)歉秫。
準(zhǔn)備
為類的靜態(tài)變量分配內(nèi)存蛾洛,并初始化其值(初始化為零值)。如果有定義其取值雁芙,且非final變量轧膘,比如
public static String test = "test";
test變量不是final變量,會(huì)被初始化為零值null兔甘,在初始化階段調(diào)用<clinit>方法時(shí)才會(huì)賦值”test”谎碍。
而一個(gè)final的靜態(tài)變量,即常量洞焙,如:
public final static String test = "test";
是會(huì)通過(guò)ConstantValue屬性在準(zhǔn)備階段就初始化為”test”蟆淀。
解析
JVM將常量池的符號(hào)引用替換為直接引用的過(guò)程拯啦。JVM規(guī)范要求在調(diào)用符號(hào)引用操作的字節(jié)碼指令之前必須先對(duì)其所使用的符號(hào)引用進(jìn)行解析。JVM會(huì)將第一次解析結(jié)果進(jìn)行緩存熔任,避免解析動(dòng)作重復(fù)進(jìn)行褒链。
初始化
初始化是類加載過(guò)程的最后一步,執(zhí)行類構(gòu)造器<clinit>()方法笋敞。
類加載器
類加載器就是通過(guò)一個(gè)類的全限定名來(lái)獲取描述此類的二進(jìn)制字節(jié)流這個(gè)動(dòng)作的模塊碱蒙。類加載器在類層次劃分荠瘪、OSGi夯巷、熱部署、代碼加密等領(lǐng)域大放異彩哀墓。
1趁餐、類與類加載器
比較兩個(gè)類是否“相等”,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提之下才有意義篮绰,包括類的Class對(duì)象的equals方法后雷、isAssignableFrom()方法、isInstance()方法吠各。
2臀突、雙親委派模型
類加載器劃分:
- 啟動(dòng)器加載器(Bootstrap ClassLoader):這個(gè)類加載器使用C++語(yǔ)言實(shí)現(xiàn)負(fù)責(zé)將< JAVA_HOME >/lib目錄中的或者被-Xbootclasspath參數(shù)指定的路徑中的,并且是虛擬機(jī)識(shí)別的類庫(kù)加載到虛擬機(jī)內(nèi)存中贾漏。啟動(dòng)類加載器無(wú)法被Java程序直接引用候学。
- 擴(kuò)展類加載器(Extension ClassLoader):這個(gè)加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),負(fù)責(zé)加載< JAVA_HOME >\lib\ext目錄中的纵散,或被java.ext.dirs系統(tǒng)變量指定路徑中的所有類庫(kù)梳码,開(kāi)發(fā)者可用直接使用擴(kuò)展類加載器。
- 應(yīng)用程序類加載器(Application ClassLoader):由sun.misc.Launcher$AppClassLoader來(lái)實(shí)現(xiàn)伍掀,這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法的返回值掰茶,所以稱它為系統(tǒng)類加載器,如果應(yīng)用程序沒(méi)有定義過(guò)自己的類加載器蜜笤,一般情況下這個(gè)就是程序中默認(rèn)的類加載器濒蒋。
雙親委派模型要求除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器把兔。其工作過(guò)程是:如果一個(gè)類加載器收到了類加載的請(qǐng)求沪伙,它首先把請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此垛贤;只有當(dāng)父加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求時(shí)焰坪,子加載器才會(huì)嘗試自己去加載。
好處:Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系聘惦。例如java.lang.Object某饰,它存放在rt.jar中儒恋,無(wú)論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給啟動(dòng)類加載器進(jìn)行加載黔漂。
3诫尽、破壞雙親委派模型
雙親委派模型主要出現(xiàn)過(guò)三次較大規(guī)模的“被破壞”情況。
- 第一次:JDK1.2引入雙親委派模型時(shí)做出妥協(xié)炬守,向前兼容
- 第二次:JNDI服務(wù)牧嫉,它的代碼有啟動(dòng)類加載器去加載,但JNDI需要調(diào)用ClassPath下的代碼减途,啟動(dòng)類加載器可能不認(rèn)識(shí)酣藻。解決:加入線程上下文類加載器,JNDI使用這個(gè)線程上下文類加載器加載所需要的SPI代碼鳍置。
- 第三次:程序動(dòng)態(tài)性辽剧。OSGi實(shí)現(xiàn)模塊化熱部署,每一個(gè)程序模塊都有一個(gè)自己的類加載器税产,當(dāng)需要更換一個(gè)Bundle時(shí)怕轿,就把這個(gè)Bundle連同類加載器一起換掉以實(shí)現(xiàn)代碼熱替換。
<clinit>方法由編譯器自動(dòng)收集類中所有靜態(tài)變量的賦值動(dòng)作以及靜態(tài)代碼塊合并生成的辟拷,按源文件中出現(xiàn)的順序(即靜態(tài)代碼塊對(duì)于其后的靜態(tài)變量撞羽,可以賦值,但不能訪問(wèn))衫冻。<clinit>方法不需要調(diào)用父類的類構(gòu)造器诀紊,JVM會(huì)保證<clinit>執(zhí)行前父類的<clinit>方法已經(jīng)執(zhí)行,所以JVM第一個(gè)執(zhí)行的<clinit>方法是Object的羽杰。JVM會(huì)保證多線程環(huán)境中<clinit>方法執(zhí)行的安全性渡紫,保證只有一個(gè)線程去執(zhí)行。
<clinit>方法不是必須的考赛,比如沒(méi)有靜態(tài)變量的接口惕澎,或沒(méi)有靜態(tài)代碼塊和靜態(tài)變量的類。
編譯期優(yōu)化
javac對(duì)代碼的運(yùn)行效率幾乎沒(méi)有任何優(yōu)化措施(JDK1.3之后-O優(yōu)化參數(shù)沒(méi)有意義了)颜骤,性能優(yōu)化主要集中在運(yùn)行期(后端的即時(shí)編譯期)唧喉,javac主要進(jìn)行了一些針對(duì)Java語(yǔ)言編碼過(guò)程的優(yōu)化,如語(yǔ)法糖忍抽。
編譯過(guò)程
編譯過(guò)程大概分為3個(gè)過(guò)程:
javac中的代碼是這樣的:
- 解析與填充符號(hào)表過(guò)程:首先進(jìn)行詞法八孝、語(yǔ)法分析,將源代碼的字符流轉(zhuǎn)換為Token集合鸠项,然后根據(jù)Token序列構(gòu)造抽象語(yǔ)法樹(shù)AST干跛,對(duì)應(yīng)parseFiles()方法;然后填充符號(hào)表(記錄符號(hào)地址和符號(hào)信息映射關(guān)系)祟绊,符號(hào)表中記錄的信息在編譯的不同階段都會(huì)用到楼入,對(duì)應(yīng)enterTress()方法哥捕;
- 插入式注解處理器的注解處理過(guò)程:處理代碼中的注解,這個(gè)過(guò)程中可能影響到語(yǔ)法樹(shù)的元素嘉熊,如果影響到了遥赚,則要重新回到解析及填充符號(hào)表的過(guò)程,這樣一個(gè)循環(huán)稱作一個(gè)Round阐肤,直到注解處理器沒(méi)有對(duì)語(yǔ)法樹(shù)進(jìn)行修改凫佛;
- 分析與字節(jié)碼生成過(guò)程:具體又分為標(biāo)注檢查(檢查變量使用前是否已生命、變量與賦值之間類型是否匹配等問(wèn)題孕惜,以及常量折疊愧薛,如”1”+”2”優(yōu)化為”12”)、數(shù)據(jù)及控制流分析(檢查局部變量使用前是否賦值诊赊、每條路徑是否都有返回值厚满、異常是否都處理了等問(wèn)題)、解語(yǔ)法糖(由desugar()方法完成)碧磅、字節(jié)碼生成(收斂生成<clinit>()方法he <init>()方法,將所有生成的信息轉(zhuǎn)換成字節(jié)碼寫入磁盤)等子過(guò)程遵馆。
語(yǔ)法糖
泛型
Java的泛型是偽泛型鲸郊,只在源碼中存在,編譯時(shí)進(jìn)行類型擦除變成原生類型(Raw Type)货邓,并在調(diào)用的地方加上強(qiáng)轉(zhuǎn)類型代碼秆撮,這是為了兼容舊版本。對(duì)于重載方法换况,如果泛型參數(shù)的泛型類型不同而其他參數(shù)以及返回類型相同职辨,是不允許重載的,比如以下方法1和方法2不能重載戈二;而如果泛型參數(shù)的泛型類型不同舒裤,且返回類型不同,則可以重載觉吭,比如方法1和方法3(JVM本來(lái)就允許)腾供。
//方法1和方法2不能重載,方法1和方法3可以重載
//方法1
public String test(List<Integer>);
//方法2
public String test(List<String>);
//方法3
public int test(List<String>);
自動(dòng)裝箱/拆箱鲜滩、遍歷循環(huán)伴鳖、變長(zhǎng)參數(shù)
遍歷循環(huán)(增強(qiáng)for)的實(shí)現(xiàn)是編譯時(shí)還原為迭代其的實(shí)現(xiàn),因此需要實(shí)現(xiàn)Iterable接口徙硅。
條件編譯
java的條件編譯通過(guò)條件為常量的if語(yǔ)句實(shí)現(xiàn)榜聂。如下面代碼中,編譯后的字節(jié)碼不會(huì)包含調(diào)用B()方法的指令嗓蘑。
if(true){
A();
} else {
B();
}
運(yùn)行期優(yōu)化
解釋器與編譯器
許多主流商用JVM包括HotSpot采用解釋器與編譯器并存的結(jié)構(gòu)须肆,啟動(dòng)的時(shí)候使用解釋器贴汪,保證啟動(dòng)速度,隨著運(yùn)行時(shí)間推移休吠,編譯器發(fā)揮作用扳埂,編譯為本地代碼,提高執(zhí)行效率瘤礁。在JVM中這種模式被稱為混合模式阳懂,可以用-Xint
強(qiáng)制JVM運(yùn)行于解釋模式,或用-Xcomp
強(qiáng)制JVM運(yùn)行于編譯模式柜思。HotSpot包含兩個(gè)及時(shí)編譯器Client Compiler和Server Compiler岩调,一般簡(jiǎn)稱為C1和C2。
熱點(diǎn)探測(cè)
運(yùn)行過(guò)程中被即時(shí)編譯器編譯的熱點(diǎn)代碼包括被多次調(diào)用的方法或#循環(huán)體赡盘,對(duì)于后者編譯器還是會(huì)以整個(gè)方法作為編譯對(duì)象号枕。
判斷方法或循環(huán)體是否熱點(diǎn)代碼的行為被稱為熱點(diǎn)探測(cè),目前主要的熱點(diǎn)探測(cè)方法有兩種:
- 基于采樣的熱點(diǎn)探測(cè)陨享。JVM周期性的檢查各個(gè)線程的棧頂葱淳,如果發(fā)現(xiàn)某些方法經(jīng)常出現(xiàn)在棧頂,那么就是熱點(diǎn)方法抛姑。缺點(diǎn)是容易收到線程阻塞或其他外界因素影響赞厕,優(yōu)點(diǎn)是簡(jiǎn)單高效;
- 基于計(jì)數(shù)器的熱點(diǎn)探測(cè)定硝。為每個(gè)方法甚至代碼塊建立計(jì)數(shù)器皿桑,統(tǒng)計(jì)執(zhí)行次數(shù),超過(guò)一定閾值就認(rèn)為是熱點(diǎn)方法蔬啡。缺點(diǎn)是不能獲取導(dǎo)方法的調(diào)用關(guān)系诲侮,優(yōu)點(diǎn)是精確且嚴(yán)謹(jǐn)。
HotSpot使用第二種箱蟆,準(zhǔn)備了方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器沟绪。前者統(tǒng)計(jì)方法被調(diào)用的次數(shù),默認(rèn)的閾值:Client模式1500次顽腾,Server模式10000次近零,可以通過(guò)-XX:CompileThreshold
來(lái)設(shè)定;后者統(tǒng)計(jì)循環(huán)體被執(zhí)行的次數(shù)抄肖,字節(jié)碼遇到控制流向后跳轉(zhuǎn)的指令稱為回邊(Back Edge)久信,通過(guò)-XX:BackEdgeThreshold
來(lái)手動(dòng)設(shè)置閾值。
對(duì)于方法調(diào)用計(jì)數(shù)器漓摩,一個(gè)方法執(zhí)行時(shí)先判斷存不存在JIT編譯過(guò)的版本裙士,存在的話執(zhí)行編譯后版本,不存在的話計(jì)數(shù)器加一管毙,再判斷是否超過(guò)閾值腿椎,超過(guò)的話向即時(shí)編譯器提交編譯申請(qǐng)桌硫。其統(tǒng)計(jì)的并不是方法被調(diào)用的絕對(duì)次數(shù),而是一段時(shí)間內(nèi)的調(diào)用次數(shù)啃炸,如果超過(guò)一定時(shí)間計(jì)數(shù)器仍不足閾值铆隘,則計(jì)數(shù)值會(huì)減少一半,這被成為熱度衰減(Counter Decay)南用,這段時(shí)間被稱為半衰期膀钠。熱度衰減的動(dòng)作時(shí)在GC時(shí)順便進(jìn)行的。
回邊計(jì)數(shù)器沒(méi)有計(jì)數(shù)熱度衰減的過(guò)程裹虫,記錄循環(huán)體被調(diào)用的絕對(duì)次數(shù)肿嘲。
默認(rèn)配置下,編譯是在后臺(tái)的編譯線程進(jìn)行的筑公,除非用-XX:-BackgroundCompilation
來(lái)禁止后臺(tái)編譯雳窟,這樣提交編譯請(qǐng)求的線程會(huì)一直等待編譯完成。
編譯優(yōu)化技術(shù)
JVM幾乎所有的優(yōu)化措施都集中在及時(shí)編譯器中匣屡。
方法內(nèi)聯(lián)
方法內(nèi)聯(lián)(Method Inlining)指的是將調(diào)用的方法代碼替換掉調(diào)用者的調(diào)用語(yǔ)句封救。目的:
- 取出調(diào)用方法的成本,如建立棧幀耸采;
- 為其他優(yōu)化建立良好基礎(chǔ)兴泥,比如內(nèi)聯(lián)可以發(fā)現(xiàn)更多的無(wú)用代碼。
考慮到多態(tài)虾宇,方法內(nèi)聯(lián)的實(shí)現(xiàn)并不簡(jiǎn)單,在編譯器無(wú)法得出調(diào)用的方法是哪個(gè)版本的結(jié)論(父類還是子類)如绸,需要在運(yùn)行期確定嘱朽。
JVM引入了類型繼承關(guān)系分析(Class Hierarchy Analysis,CHA)技術(shù)怔接,用于確定目前加載的類中某個(gè)接口是否有多于一種的實(shí)現(xiàn)搪泳、某類是否存在子類、子類是否抽象等信息扼脐。進(jìn)行內(nèi)聯(lián)時(shí):
- 如果目標(biāo)方法是非虛方法(私有方法岸军、實(shí)力構(gòu)造器、父類方法瓦侮、靜態(tài)方法等)艰赞,那么直接進(jìn)行內(nèi)聯(lián);
- 對(duì)于虛方法肚吏,向CHA查詢?cè)摲椒ㄊ欠裼卸鄠€(gè)版本可選方妖,如果只有一個(gè)版本,則直接進(jìn)行內(nèi)聯(lián)罚攀,此時(shí)屬于激進(jìn)優(yōu)化党觅,需要預(yù)留逃生門(守護(hù)內(nèi)聯(lián))雌澄,此后如果JVM沒(méi)有加載到改變方法接受者的繼承關(guān)系的類,則可以繼續(xù)使用內(nèi)聯(lián)優(yōu)化的版本杯瞻,否則拋棄已編譯的代碼镐牺、退回到解釋狀態(tài)進(jìn)行或重新編譯;
- 如果虛方法有多個(gè)版本魁莉,則嘗試內(nèi)聯(lián)緩存(Inline Cache)睬涧。發(fā)生方法調(diào)用前,內(nèi)聯(lián)緩存狀態(tài)為空沛厨;第一次調(diào)用后宙地,緩存記錄下方法接受者的版本信息,每次進(jìn)行方法調(diào)用的時(shí)候都比較接受者版本逆皮,如果方法接受者版本一樣宅粥,則繼續(xù)調(diào)用內(nèi)聯(lián)緩存進(jìn)行內(nèi)聯(lián),否則取消內(nèi)聯(lián)电谣。
冗余訪問(wèn)消除
冗余訪問(wèn)消除(Redundant Loads Elimination)指的是如果能保證一個(gè)方法的兩次調(diào)用之間的代碼不會(huì)引起其返回值的更改秽梅,那么這第二次調(diào)用的結(jié)果可以直接用第一次調(diào)用結(jié)果去賦值,比如一下代碼:
public void foo1(){
y=b.value;
//其他調(diào)用剿牺,不會(huì)影響b.value的返回值
z=b.value();
}
以上代碼可以優(yōu)化為:
public void foo1(){
y=b.value;
//其他調(diào)用企垦,不會(huì)影響b.value的返回值
z=y;
}
復(fù)寫傳播
復(fù)寫傳播(Copy Propagation)指的是去掉重復(fù)的變量。
無(wú)用代碼消除
無(wú)用代碼消除(Dead Code Elimination)晒来,無(wú)用代碼指的是永遠(yuǎn)不會(huì)izhixing的代碼钞诡,或者完全沒(méi)有意義的代碼。
公共子表達(dá)式消除
Common Subexpression Elimination定義:一個(gè)表達(dá)式E已經(jīng)計(jì)算過(guò)湃崩,且計(jì)算后到現(xiàn)在E的變量全部沒(méi)有變化荧降,那么E這次出現(xiàn)成為了公共子表達(dá)式,無(wú)需重復(fù)計(jì)算攒读,直接用前面計(jì)算結(jié)果替換即可朵诫。如果消除優(yōu)化僅限于程序基本塊內(nèi),則成為局部公共子表達(dá)式消除薄扁,如果覆蓋范圍涵蓋多個(gè)基本塊剪返,則成為全局公共子表達(dá)式消除。
數(shù)組邊界檢查消除
Java訪問(wèn)數(shù)組元素時(shí)邓梅,會(huì)對(duì)下標(biāo)進(jìn)行上下界范圍檢查脱盲,不滿足上下界時(shí)會(huì)拋出ArrayIndexOutOfBoundsException異常。
編譯器根據(jù)數(shù)據(jù)流分析確定數(shù)組長(zhǎng)度震放,并判斷小表有無(wú)月結(jié)宾毒;在循環(huán)中進(jìn)行數(shù)組訪問(wèn)時(shí),也是可以通過(guò)數(shù)據(jù)流分析判定循環(huán)變量的取值是否越界,如果能保證循環(huán)體中不越界的話循環(huán)體中訪問(wèn)數(shù)組的語(yǔ)句可以消除邊界檢查诈铛。
還有一種思路時(shí)隱式異常處理乙各,將空指針檢查和除數(shù)為零檢查消除,注冊(cè)一個(gè)Segment Fault信號(hào)的異常處理器幢竹,放在異常處理里面耳峦,在這個(gè)異常處理器里面再轉(zhuǎn)換為對(duì)應(yīng)的異常并拋出。
還有一些其他的消除操作焕毫,比如自動(dòng)裝箱消除蹲坷、安全點(diǎn)消除、消除反射等等邑飒。
逃逸分析
逃逸分析不能直接優(yōu)化代碼循签,而是為其他優(yōu)化手段提供優(yōu)化的依據(jù)。逃逸分析指的是分析對(duì)象動(dòng)態(tài)作用域:一個(gè)對(duì)象在方法中被定義后疙咸,被外部方法引用县匠,則稱為方法逃逸,被外部線程引用訪問(wèn)到的話撒轮,被稱為線程逃逸乞旦。如果能證明一個(gè)對(duì)象不會(huì)逃逸到方法或線程外,則可以進(jìn)行以下優(yōu)化:
- 棧上分配(Stack Allocation):若確認(rèn)對(duì)象沒(méi)有方法逃逸题山,可以將其在棧上分配內(nèi)存兰粉,則其占用內(nèi)存會(huì)隨著棧幀出棧而被銷毀,減少GC壓力顶瞳,而一般應(yīng)用中不逃逸的局部對(duì)象占很大比例玖姑;
- 同步消除(Synchronization Elimination):若確認(rèn)對(duì)象沒(méi)有線程逃逸,可以對(duì)該變量實(shí)時(shí)的同步措施消除慨菱;
- 標(biāo)量替換(Scalar Replacement):標(biāo)量指一個(gè)數(shù)據(jù)無(wú)法再分解為更小的數(shù)據(jù)來(lái)表示客峭,如基礎(chǔ)數(shù)據(jù)類型,反之稱之為聚合量(Aggregate)抡柿,如對(duì)象。若確認(rèn)一個(gè)對(duì)象沒(méi)有逃逸等恐,則可以不創(chuàng)建對(duì)象洲劣,改為直接創(chuàng)建它會(huì)被使用到的成員變量來(lái)代替,同時(shí)可以保存在棧上课蔬,提高讀寫效率囱稽,并為進(jìn)一步優(yōu)化創(chuàng)造條件。