關於JVM的逃逸分析
何謂“逃逸”?
我們都知道Java中的物件預設是分配到堆上的,垃圾回收機制也會回收堆中不再使用的物件,但在此之前需要篩選可回收的物件,因此會造成,回收物件還有整理記憶體,都比較耗時間,開銷也是非常之大。而此也是Java語言被瘋狂吐槽的一地方,就是Java不支援棧上分配物件。而在我們日常開發中,記憶體,時間都是相當的寶貴,如何優化成為在開發中一個不可或缺的環節。
逃逸分析(Escape Analysis),是一種可以有效減少Java 程式中同步負載和記憶體堆分配壓力的跨函式全域性資料流分析演算法。通過逃逸分析,Java Hotspot編譯器能夠分析出一個新的物件的引用的使用範圍從而決定是否要將這個物件分配到堆上。 逃逸分析算是目前Java虛擬機器中比較前沿的優化技術了,但至於適不適合,需要據實際情況而定了。
在計算機語言編譯器優化原理中,逃逸分析是指分析指標動態範圍的方法,它同編譯器優化原理的指標分析和外形分析相關聯。當變數(或者物件)在方法中分配後,其指標有可能被返回或者被全域性引用,這樣就會被其他方法或者執行緒所引用,這種現象稱作指標(或者引用)的逃逸(Escape)。通俗點講,如果一個物件的指標被多個方法或者執行緒引用時,那麼我們就稱這個物件的指標發生了逃逸。
網上有位博友這麼形容逃逸分析,用了一段簡單直接的程式碼,請看:
public StringBuilder escapeDemo1(System a, System b) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(a); stringBuilder.append(b); return stringBuilder; }
StringBuilder是方法的一個內部變數,而此時將它直接返回,這樣StringBuilder就有可能被其他地方的方法或變數改變,這樣它的作用域就不只是demo1方法了,雖然它是一個區域性變數,但其發生了“逃逸事故”。
那麼,我可以改一下程式碼:
public String escapeDemo2(System a, System b) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(a); stringBuilder.append(b); return stringBuilder.toString(); }
如此再沒有返回StringBuilder,而是toString(),那麼StringBuilder沒有從方法中脫離,將不會發生逃逸。
換種方式理解吧,因為Java本身的限制(物件只能分配到堆中),我可以這麼操作了,為了減少臨時物件在堆內分配的數量,我會在一個方法體內定義一個區域性變數,並且該變數在方法執行過程中未發生逃逸,按照JVM調優機制,首先會在堆記憶體建立類的例項,然後將此物件的引用壓入呼叫棧,繼續執行,這是JVM優化前的方式。然後,我採用逃逸分析對JVM進行優化。即針對棧的重新分配方式,首先找出未逃逸的變數,將該變數直接存到棧裡,無需進入堆,分配完成後,繼續呼叫棧內執行,最後執行緒執行結束,棧空間被回收,區域性變數也被回收了。如此操作,是優化前在堆中,優化後在棧中,從而減少了堆中物件的分配和銷燬,從而優化效能。
但是逃逸分析會有時間消耗,所以效能不一定會有提升,並且由於逃逸分析比較耗時,目前的實現都是採用不那麼準確但是時間壓力相對較小的演算法來完成逃逸分析,這就有可能導致效果不穩定,所以,要根據實際情況,酌情處理。
一項技術的好壞,不是憑嘴說說,適合自己的才是最好的。
ok,純屬結合專案實際,整理前人文言,權當學習手札。
你有興趣,推薦你以下好文:
ofollow,noindex">淺談HotSpot逃逸分析