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