JVM中逃逸分析和標量替換和棧上分配和同步消除

最近也是疫情嚴重,呆在家裏把JVM虛擬機內容慢慢又過一遍,把一些內容再做個總結加深下自己印象。

逃逸分析:

逃逸分析其實就是分析一個方法中創建出來的‘對象’的作用域是否限制於本方法內。單例模式就是一個典型的逃逸分析。

翻譯:假設你正在跑步機跑步(這是一個running方法),這時候餓了(有需要),讓媽媽給你拿一碟小番茄(在另外一個方法裏 new 小番茄 = 小番茄() );你把一碟都拿走了,這就是逃逸分析。意思就是媽媽創建出來的小番茄對象必須整個對象給你,所以new出來的那個對象就逃逸出了媽媽方法的控制。

上代碼:
在這裏插入圖片描述
可以看出main類裏面有個getDemo方法,裏面創建並且返回了一個Main類型的demo對象,然後main方法接受到了main類型的demo對象,這就是逃逸了。
那怎麼樣纔算不發生逃逸呢?先來看兩個概念。

標量和聚合量:

標量就是不能再被分解的量。(例如八大基本類型 byte , short , int ,long ,char , float ,double , boolean ).
聚合量就是還能繼續被分解的量,例如對象能被分解成一個個基本類型數據。
還是上面那個例子,Main類型的demo對象是聚合量,int name 和 int age 就是標量;

那麼,怎麼樣纔算是不發生逃逸呢?

標量替換:

有時候我們返回一個對象,其實也只是想用對象裏面的某個屬性而已,所以這時候我們可以寫成:
在這裏插入圖片描述
這個就是標量替換,方法不直接返回對象類型,而是返回一個基本類型,也就是標量,那麼new出來的對象的作用域就還是在getDemo方法裏面,不會跑到main方法去。

震驚?!不是所有的對象都在堆上分配內存空間

棧上分配:

只有發生了逃逸的對象,纔會在堆上分配內存,JIT即使編譯技術優化中加入了逃逸分析,就是專門分析對象是否發生逃逸的,如果發現對象的作用域僅限於本方法內,也就是外部沒有任何引用,那麼就讓對象在棧幀執行到該方法時在棧上分配內存。隨着方法執行完畢,返回一個標量基本數據類型後,棧幀彈出棧,所有分配的內存回收,當然也包括那個對象的內存,所以這效率肯定比GC不知道啥時候纔來打掃衛生要高得多。

同步消除:

這裏消除的其實是對象的同步鎖,堆被線程共享,那麼堆上面的對象也會被線程共享,大家一起讀寫,會出現同步的併發問題,在另外一篇的博客說的買票就是這麼回事,所以對象也加了synchronized同步鎖。但是棧是線程獨享的,如果進行棧上分配,那麼就根本不需要同步鎖,提高了執行效率。這就是同步消除。

做逃逸分析有什麼好處:

經過上面的總結可以發現:
(1)消除了同步鎖,提高了運行速度。
(2)減輕了垃圾收集子系統GC的負擔,標量替換使得減少了對象回收的一串漫長流程。
(3)提高了內存利用率,棧上分配可以更好管理內存使用和回收。

震驚?!這麼多好處!JDK中逃逸分析居然還不成熟?!

這個概念書上說1999年就提出來了,是非常好的一個想法,但是直到現在也還不成熟,因爲代碼編寫的原因,沒有嚴格限制返回類型,圖方便我也會整個對象返回。又因爲整個逃逸分析流程較長(整套逃逸分析,標量替換,棧上分配,同步消除,整套大寶劍做下來也很耗費性能資源)。結果就造成整套分析下來全部對象還要乖乖在堆上分配內存。速度更慢了。
但是這是即使編譯技術JIT的非常好的一個模式,相信將來會有更大的發展空間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章