中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM優化之逃逸分析與分配消除

發布時間:2020-07-07 21:52:22 來源:網絡 閱讀:216 作者:艾弗森哇 欄目:編程語言

要了解逃逸分析背后的基本原理,我們先來看下這段有問題的C代碼——當然這個是沒法用Java來寫的:

JVM優化之逃逸分析與分配消除

這段C代碼在棧上創建了一個int類型的變量,然后把它的指針作為函數的返回值返回了。這樣做是有問題的,因為當gettheint()函數返回的時候,int所在的棧幀就已經被銷毀了,后面你再去訪問這個地址的話,就不知道里面存儲的到底是什么了。

Java平臺設計的一個主要目標就是要消除這種類型的bug。從設計上,JVM就不具備這種低級的“根據位置索引來讀內存”的能力。這類操作對應的Java字節碼是putfield和getfield。

來看下這段Java代碼:

JVM優化之逃逸分析與分配消除

這段代碼創建了一億對隨機大小的矩形,并去計算有多少對是大小一樣的。每次迭代都會創建一對新的矩形。你可能會認為main方法里會創建2億個Rect對象:一億個r1,一億個r2。

不過,如果某個對象只是在方法內部創建并使用的話——也就是說,它不會傳遞到另一個方法中或者作為返回值返回——那么運行時程序就還能做得更聰明一些。你可以說這個對象是沒有逃逸出去的,因此運行時(其實就是JIT編譯器)做的這個分析又叫做逃逸分析。

如果一個對象沒有逃逸出去,那也就是說JVM可以針對這個對象做一些類似“棧自動分配”的事情。在這個例子當中,這個對象不會從堆上分配空間,因此它也不需要垃圾回收器來回收。一旦使用這個“棧分配(stack-allocated)”對象的方法返回了,這個對象所占用的內存也就自動被釋放掉了。

事實上,HotSpot VM的C2編譯器做的事情要比棧分配要復雜得多。我們現在就來看一下。

在HotSpot VM的源碼中,可以看到逃逸分析系統是如何對對象的使用進行分類的:

JVM優化之逃逸分析與分配消除

第一類說明這個對象可以用標量來代替。這種分配消除技術叫標量替換(scalar replacement)。這意味著這個對象會被拆解成它的構成字段,這就相當于分配對象的操作變成了在方法內部創建多個局部變量。完成這個之后,另一項HotSpot VM的JIT技術會參與進來,它會將這些字段(事實上已經是局部變量了)存儲到CPU的寄存器中(如果有必要就存儲在棧上)。

Java平臺的主要挑戰是執行模型非常復雜。在上述例子中,如果只看源代碼,你會認為r1對象是不會逃逸出main方法外的,但r2會作為參數傳給r1的sameArea方法,因此它逃逸出了main方法外。

根據上面的分類,乍一看的話r1應該歸類為NoEscape,而r2應該歸為ArgEscape;不過這個結論是錯誤的,原因有幾點。

第一,回想一下,Java中的方法調用最終會通過編譯器替換為字節碼invoke。它會把調用目標(也就是接收對象,注:即要調用的對象)和入參填充到棧中,然后查找到這個方法,再分發給它(也就是執行這個方法)。

這意味著接收對象也被傳入了調用的方法中(它就是調用的方法里的this對象)。因此接收對象也逃逸出了當前域;在這個例子中,這意味著如果逃逸分析分析完這段Java代碼,r1和r2都會歸類為ArgEscape。

如果就只是這樣的話,那么分配消除的使用場景就很有限了。所幸的是,HotSpot VM能做得更好。我們來仔細看一下它的字節碼,看看能發現什么。

sameArea()方法很小(只有17個字節的字節碼),在本例中也會被頻繁調用,因此它是方法內聯(method inlined)的一個理想對象。

JVM優化之逃逸分析與分配消除

這個方法又調用了兩次area()方法(這個也是可以內聯的):

JVM優化之逃逸分析與分配消除

通過JITWatch或者PrintCompilation可以看到,area()方法的調用的確被內聯進了調用方sameArea()方法里,而sameArea()又被內聯到了main()方法的循環體中。JITWatch為內聯方法提供了一個很方便的圖形化展示(如圖一所示)。

JVM優化之逃逸分析與分配消除

請記住Java HotSpot VM的JIT編譯器的優化順序也是很重要的。方法內聯是最早的優化,也被稱為網關優化(gateway optimization),因為它首先把相關聯的代碼都聚合在了一起,為其它優化打開了大門。http://dalian.huodong.dqccc.com/exposition/detail-2237268.html

鄭州男人不孕不育醫院:http://www.xbzztj.com/

現在sameArea()方法和area()方法都被內聯進來了,方法域的問題不復存在,所有的變量都只在main方法的作用域內了。也就是說逃逸分析不會再把r1和r2視作ArgEscape類型:方法內聯之后,它們現在都被歸類為NoEscape。

這個結果看起來可能有悖常理,不過你需要記住的是JIT編譯器并不是通過原始代碼來進行優化的。如果不知道這點,就搞不清楚哪些情況能夠進行逃逸分析。

前面的例子中,這些對象的分配都不會在堆上進行了,會把它們的字段拆解成獨立的值。寄存器分配器通常會把拆解出來的字段直接放到寄存器中,不過如果沒有足夠可用的寄存器,那剩下的字段會被存儲到棧上。這種情況被稱為棧溢出(stack spill,注:和stack overflow不同)。

在逃逸分析開啟和關閉的模式下分別運行這個程序,再觀察下GC的活動,你就能看到密集循環中堆分配消除的巨大威力。

在現代JVM中逃逸分析是默認開啟的,得通過JVM參數-XX:-DoEscapeAnalysis來關掉它。

下面是開啟了逃逸分析之后的GC日志(一些細節刪除了):

JVM優化之逃逸分析與分配消除

從日志中可以看到根本沒有發生GC事件——只是在進程退出時往日志里記錄了下堆的摘要信息。如果再看下關閉逃逸分析后的運行日志,情況就截然不同了:

JVM優化之逃逸分析與分配消除

這里可以很清楚地看到,由于Eden區空間滿了,導致了內存分配失敗、需要進行垃圾回收,因此觸發了GC事件。

結論

逃逸分析是Java HotSpot VM引入的一項非常有用的升級。這項功能仍在開發階段時,實際測試中它帶來的性能提升就有3%到6%。

對于那些對平臺特性的實現過程和原理感興趣的開發人員來說,逃逸分析有個很有意思的特點:這項特性依賴于其它優化(自動內聯),不然用處不大。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

辛集市| 肃宁县| 苏尼特右旗| 丰顺县| 宜川县| 河北省| 合山市| 钟祥市| 城口县| 新绛县| 胶南市| 教育| 平和县| 综艺| 临桂县| 融水| 侯马市| 湘潭市| 益阳市| 子长县| 察雅县| 麻栗坡县| 福海县| 两当县| 隆化县| 昌图县| 陈巴尔虎旗| 石首市| 保康县| 松桃| 贡山| 桃江县| 江陵县| 开鲁县| 北辰区| 晴隆县| 南平市| 连山| 开封市| 建德市| 美姑县|