摘要:當(dāng)一個(gè)對(duì)象被定義之后,可能會(huì)被外部對(duì)象引用,稱之為方法逃逸也有可能被其他線程所引用,稱之為線程逃逸。在編譯過(guò)程中,經(jīng)過(guò)逃逸分析確定一個(gè)對(duì)象不會(huì)被其他線程或者方法訪問(wèn),那么會(huì)將對(duì)象的創(chuàng)建替換成為多個(gè)成員變量的創(chuàng)建,稱之為標(biāo)量替換。
1.引言
Java 程序運(yùn)行時(shí),JVM 會(huì)將 .class 字節(jié)碼轉(zhuǎn)換成機(jī)器能夠識(shí)別的指令,指令轉(zhuǎn)換過(guò)程會(huì)產(chǎn)生耗時(shí),延緩程序的運(yùn)行速度,為了解決這種問(wèn)題出現(xiàn)了「JIT(即時(shí)編譯)」技術(shù)。JIT 主要有兩個(gè)功能:
緩存「Hot Spot Code(熱點(diǎn)代碼:頻繁運(yùn)行的方法或代碼塊)」對(duì)應(yīng)的機(jī)器指令,方便下次調(diào)用。
代碼編譯優(yōu)化。
而在 JIT 的代碼優(yōu)化過(guò)程中,最重要的就是「逃逸分析(Escape Analysis)」。
2. 逃逸分析逃逸分析就是 分析Java對(duì)象的動(dòng)態(tài)作用域。當(dāng)一個(gè)對(duì)象被定義之后,可能會(huì)被外部對(duì)象引用,稱之為「方法逃逸」;也有可能被其他線程所引用,稱之為「線程逃逸」。
public class EscapeObject { public static String createStr() { String sb = "hello world!"; return sb; } }
例如上面這段代碼將創(chuàng)建的字符串對(duì)象 sb 返回,這樣可以被其他方法或線程引用。
public class EscapeObject { public static String createStr() { StringBuffer sb = new StringBuffer("hello world!"); return sb.toString(); } }
如果這樣實(shí)現(xiàn)的話,sb 對(duì)象就沒(méi)有「逃逸」。
利用逃逸分析,編譯器可以對(duì)代碼做如下優(yōu)化:
同步省略
標(biāo)量替換
棧上分配
2.1 同步省略在 JIT 編譯過(guò)程中,如果發(fā)現(xiàn)一個(gè)對(duì)象不會(huì)被多線程訪問(wèn),那么針對(duì)這個(gè)對(duì)象的同步措施就可以省略掉,即「鎖銷除」。例如 Vector 和 StringBuffer 這樣的類,它們中的很多方法都是有鎖的,當(dāng)某個(gè)對(duì)象確定是線程安全的情況下,JIT編譯器會(huì)在編譯這段代碼時(shí)進(jìn)行鎖銷除來(lái)提升效率。
2.2 標(biāo)量替換「標(biāo)量(Scalar)」是指無(wú)法再分解成更小粒度的數(shù)據(jù),例如 Java 中的原始數(shù)據(jù)類型(int,long等),相對(duì)如果一個(gè)數(shù)據(jù)可以繼續(xù)分解,則稱之為「聚合量(Aggregate)」,例如 Java對(duì)象。在 JIT 編譯過(guò)程中,經(jīng)過(guò)逃逸分析確定一個(gè)對(duì)象不會(huì)被其他線程或者方法訪問(wèn),那么會(huì)將對(duì)象的創(chuàng)建替換成為多個(gè)成員變量的創(chuàng)建,稱之為「標(biāo)量替換」。
public class EscapeObject { private static void getUser() { User user = new User("張三", 18); System.out.println("user name is " + user.name + ", age is " + user.age); } public static void main(String[] args) { getUser(); } } class User { String name; int age; public User(String name, int age) { this.name = name; this.age = age; } }
上面這段代碼中,對(duì)象 user 只會(huì)在getUser()方法中被調(diào)用,那么 JIT動(dòng)態(tài)編譯時(shí),不會(huì)創(chuàng)建對(duì)象 user,而之創(chuàng)建它的兩個(gè)成員變量 name 和 age,類似:
private static void getUser() { String name = "張三"; int age = 18; System.out.println("user name is " + user.name + ", age is " + user.age); } public static void main(String[] args) { getUser(); }
標(biāo)量替換減少了創(chuàng)建對(duì)象需要的堆內(nèi)存,同時(shí)也不用進(jìn)行 GC。
2.3 棧上分配「棧上分配」是指對(duì)象和數(shù)據(jù)不是創(chuàng)建在堆上,而是創(chuàng)建在棧上,隨著方法的結(jié)束自動(dòng)銷毀。但實(shí)際上,JVM 例如常用的「HotSpot」虛擬機(jī)并沒(méi)有實(shí)現(xiàn)棧上分配,實(shí)際是用「標(biāo)量替換」代替實(shí)現(xiàn)的。
在 JAVA 中,對(duì)象只分配在堆中:
The heap is the runtime data area from which memory for all class instances and arrays is allocated。 堆是所有的對(duì)象實(shí)例以及數(shù)組分配內(nèi)存的運(yùn)行時(shí)數(shù)據(jù)區(qū)域。2.4 如何開(kāi)啟逃逸分析
可以通過(guò)設(shè)置 JVM 參數(shù)來(lái)開(kāi)啟或關(guān)閉逃逸分析
-XX:+DoEscapeAnalysis :開(kāi)啟逃逸分析(從JDK1.7開(kāi)始默認(rèn)開(kāi)啟)
-XX:-DoEscapeAnalysis :關(guān)閉逃逸分析
3. 參考資料深入理解Java中的逃逸分析
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/71862.html
摘要:記得幾年前有一次棧長(zhǎng)去面試,問(wèn)到了這么一個(gè)問(wèn)題中的對(duì)象都是在堆中分配嗎說(shuō)明為什么當(dāng)時(shí)我被問(wèn)得一臉蒙逼,瞬間被秒殺得體無(wú)完膚,當(dāng)時(shí)我壓根就不知道他在考什么知識(shí)點(diǎn),難道對(duì)象不是在堆中分配嗎最后就沒(méi)然后了,回去等通知了。。 記得幾年前有一次棧長(zhǎng)去面試,問(wèn)到了這么一個(gè)問(wèn)題: Java中的對(duì)象都是在堆中分配嗎?說(shuō)明為什么! 當(dāng)時(shí)我被問(wèn)得一臉蒙逼,瞬間被秒殺得體無(wú)完膚,當(dāng)時(shí)我壓根就不知道他在考什么...
摘要:在一般應(yīng)用中,不會(huì)逃逸的局部對(duì)象所占的比例很大,如果能使用棧上分配,那大量的對(duì)象就會(huì)隨著方法的結(jié)束而自動(dòng)銷毀了,垃圾收集系統(tǒng)的壓力將會(huì)小很多。相關(guān)參數(shù)設(shè)置大對(duì)象直接進(jìn)入年老代的閾值,當(dāng)對(duì)象大小超過(guò)這個(gè)值時(shí),將直接在年老代分配。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card t...
摘要:被多次執(zhí)行的循環(huán)體。數(shù)組范圍檢查消除。這種安全檢查策略可以避免溢出。不過(guò),虛擬機(jī)還是挺聰明的,它會(huì)根據(jù)運(yùn)行期收集到的信息來(lái)自動(dòng)選擇最優(yōu)方案。 1.解釋器與JIT編譯器 首先我們先來(lái)了解一下運(yùn)行在虛擬機(jī)之上的解釋器與JIT編譯器。 當(dāng)我們的虛擬機(jī)在運(yùn)行一個(gè)java程序的時(shí)候,它可以采用兩種方式來(lái)運(yùn)行這個(gè)java程序: 采用解釋器的形式,也就是說(shuō),在運(yùn)行.class運(yùn)行的時(shí)候,解釋器一邊...
摘要:虛擬機(jī)在執(zhí)行程序的過(guò)程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域。棧幀棧幀是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。棧幀的概念結(jié)構(gòu)如下運(yùn)行時(shí)數(shù)據(jù)區(qū)腦圖高 這里我們先說(shuō)句題外話,相信大家在面試中經(jīng)常被問(wèn)到介紹Java內(nèi)存模型,我在面試別人時(shí)也會(huì)經(jīng)常問(wèn)這個(gè)問(wèn)題。但是,往往都會(huì)令我比較尷尬,我還話音未落,面試者就會(huì)背誦一段(Java虛擬...
摘要:解釋器與編譯器并存如果選用完全解釋策略,那么編譯器將停止所有的工作,字節(jié)碼將完全依靠解釋器逐行解釋執(zhí)行。如果選用完全編譯策略,那么解釋器仍然會(huì)在編譯器無(wú)法進(jìn)行的特殊情況下介入運(yùn)行,這主要是確保程序能夠最終順序執(zhí)行。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card table數(shù)據(jù)...
閱讀 3270·2021-10-27 14:20
閱讀 2537·2021-10-08 10:05
閱讀 1636·2021-09-09 09:33
閱讀 2911·2019-08-30 13:16
閱讀 1445·2019-08-29 18:34
閱讀 1182·2019-08-29 10:58
閱讀 1236·2019-08-28 18:22
閱讀 1235·2019-08-26 13:33