JVM內存增強之逃逸分析
概念
逃逸分析一種數(shù)據(jù)分析算法,基于此算法可以有效減少 Java 對象在堆內存中的分配。 Hotspot 虛擬機的編譯器能夠分析出一個新對象的引用范圍,然后決定是否要將這個對象分配到堆上.
當一個對象在方法中被定義后,對象只在方法內部使用,則認為沒有發(fā)生逃逸。
當一個對象在方法中被定義后,它被外部方法所引用,則認為發(fā)生逃逸。
//對象發(fā)生了逃逸,不會在棧上分配,有可能導致GC STW public StringBuffer append(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb; } //對象未發(fā)生逃逸 public String append(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb.toString(); }
建議:開發(fā)中能在方法內部應用對象的,就盡量控制在內部
逃逸分析參數(shù)設計
在 JDK 1.7 版本之后, HotSpot 中默認就已經開啟了逃逸分析,如果使用的是較早的
版本,開發(fā)人員則可以通過:
? 選項“ -XX:+DoEscapeAnalysis" 顯式開啟逃逸分析。
? 通過選項“ -XX:+PrintEscapeAnalysis" 查看逃逸分析的篩選結果。
使用逃逸分析
編譯器可以對代碼做如下優(yōu)化
1.棧上分配:將堆分配轉化為棧分配。如果一個對象在方法內創(chuàng)建,要使指向該對象的引用不會發(fā)生逃逸,對象可能是棧上分配的候選
/** * 棧上分配測試(-XX:+DoEscapeAnalysis) * -Xmx128m -Xms128m -XX:+DoEscapeAnalysis -XX:+PrintGC */ public class ObjectStackAllocationTests { public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } long end = System.currentTimeMillis(); System.out.println("花費的時間為: " + (end - start) + " ms"); // 為了方便查看堆內存中對象個數(shù),線程 sleep TimeUnit.MINUTES.sleep(5); } private static void alloc() { byte[] data = new byte[10];//未發(fā)生逃逸 } }
2.同步鎖消除:
我們知道線程同步是靠犧牲性能來保證數(shù)據(jù)的正確性,這個過程的代價會非常高。程序 的并發(fā)行和性能都會降低。JVM 的 JIT 編譯器可以借助逃逸分析來判斷同步塊所使用的鎖對象是否只能夠被一個線程應用?假如是,那么 JIT 編譯器在編譯這個同步塊的時候就會取消對這部分代碼上加的鎖。這個取消同步的過程就叫同步省略,也叫鎖消除
public class SynchronizedLockTest { public void lock() { Object obj= new Object(); synchronized(obj) { System.out.println(obj); } }
3.標量替換分析
所謂的標量(scalar)一般指的是一個無法再分解成更小數(shù)據(jù)的數(shù)據(jù)。例如,Java 中 的原始數(shù)據(jù)類型就是標量。相對的,那些還可以分解的數(shù)據(jù)叫做聚合量(Aggregate),Java 中的對象就是聚合量,因為他可以分解成其他聚合量和標量。在 JIT 階段,如果經過逃逸分析,發(fā)現(xiàn)一個對象不會被外界訪問的話,那么經過 JIT 優(yōu)化,就會把這個對象分解成若干個變量來代替。這個過程就是標量替換。
public class ObjectScalarReplaceTests { public static void main(String args[]) { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } long end = System.currentTimeMillis(); System.out.println("花費的時間為: " + (end - start) + " ms"); } private static void alloc() { Point point = new Point(1,2); } static class Point { private int x; private int y; public Point(int x,int y){ this.x=x; this.y=y; } } //對于上面代碼,假如開啟了標量替換,那么 alloc 方法的內容就會變?yōu)槿缦滦问? private static void alloc() { int x=10; int y=20; }
alloc 方法內部的 Point 對象是一個聚合量,這個聚合量經過逃逸分析后,發(fā)現(xiàn)他并沒有逃逸,就被替換成兩個標量了。那么標量替換有什么好處呢?可以大大減少堆內存的占用。因為一旦不需要創(chuàng)建對象了,那么就不再需要分配堆內存了。標量替換為棧上分配 提供了很好的基礎。
FAQ
1.什么是逃逸分析?
可以有效減少 Java 對象在堆內存中的分配壓力和同步負載的算法
2.逃逸分析有什么優(yōu)勢、劣勢?
逃逸分析是需要消耗一定的性能去執(zhí)行分析的,所以說如果方法中的對象全都是處于逃逸狀態(tài),那么就沒有起到優(yōu)化的作用,從而就白白損失了這部分的性能消耗
到此這篇關于JVM內存增強之逃逸分析的文章就介紹到這了,更多相關JVM逃逸內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決SpringMvc后臺接收json數(shù)據(jù)中文亂碼問題的幾種方法
本篇文章主要介紹了解決SpringMvc后臺接收json數(shù)據(jù)中文亂碼問題的幾種方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01Java中SynchronousQueue的底層實現(xiàn)原理剖析
BlockingQueue的實現(xiàn)類中,有一種阻塞隊列比較特殊,就是SynchronousQueue(同步移交隊列),隊列長度為0。本文就來剖析一下SynchronousQueue的底層實現(xiàn)原理,感興趣的可以了解一下2022-11-11Spring Boot Maven 打包可執(zhí)行Jar文件的實現(xiàn)方法
這篇文章主要介紹了Spring Boot Maven 打包可執(zhí)行Jar文件的實現(xiàn)方法,需要的朋友可以參考下2018-02-02Spring擴展點之BeanFactoryPostProcessor詳解
這篇文章主要介紹了Spring擴展點之BeanFactoryPostProcessor詳解,Spring的設計非常優(yōu)雅,有很多的擴展點供我們對項目進行擴展,今天學習一下Spring其中擴展點之一的BeanFactoryPostProcessor,需要的朋友可以參考下2023-11-11JVM?運行時數(shù)據(jù)區(qū)與JMM?內存模型
這篇文章主要介紹了JVM?運行時數(shù)據(jù)區(qū)與JMM?內存模型,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值。需要的朋友可以參考一下2022-07-07