1. 程式人生 > >【效能優化】面試官:Java中的物件都是在堆上分配的嗎?

【效能優化】面試官:Java中的物件都是在堆上分配的嗎?

## 寫在前面 > 從開始學習Java的時候,我們就接觸了這樣一種觀點:Java中的物件是在堆上建立的,物件的引用是放在棧裡的,那這個觀點就真的是正確的嗎?如果是正確的,那麼,面試官為啥會問:“Java中的物件就一定是在堆上分配的嗎?”這個問題呢?看來,我們從接觸Java就被灌輸的這個觀點值得我們懷疑。 ## 關於面試題 標題中的面試題為:Java中的物件都是在堆上分配的嗎? 面試官這樣問,有些小夥伴心裡會想:我從一開始學習Java時,就知道了:Java中的物件是在堆上建立的,物件的引用是儲存到棧中的,那Java中的物件是在堆上分配的啊!難道不是嗎? ![](https://img-blog.csdnimg.cn/20200921233555767.jpg) 如果你這樣回答,就會被直接Pass掉。 或許有些小夥伴還是不太明白,那我們繼續往下看。 ## 面試題答案 首先,我們先給出這個題目的答案,這裡我先簡短的回答下這個面試題,後續我們會進行相關分析。 **你可以這樣回答:Java中的物件不一定是在堆上分配的,因為JVM通過逃逸分析,能夠分析出一個新物件的使用範圍,並以此確定是否要將這個物件分配到堆上。** 這裡,我們接觸了一個新名詞:逃逸分析。相信很多小夥伴不是很明白,那我們繼續往下看。 ![](https://img-blog.csdnimg.cn/20200921233609611.jpg) ## 逃逸分析 ### 逃逸分析的概念 先以官方的形式來說下什麼是逃逸分析。逃逸分析就是:一種確定指標動態範圍的靜態分析,它可以分析在程式的哪些地方可以訪問到指標。 在JVM的即時編譯語境下,逃逸分析將判斷新建的物件是否逃逸。即時編譯判斷物件是否逃逸的依據:一種是物件是否被存入堆中(靜態欄位或者堆中物件的例項欄位),另一種就是物件是否被傳入未知程式碼。 直接說這些概念,確實有點暈啊,那我們就來兩個示例。 ![](https://img-blog.csdnimg.cn/20200921233622197.jpg) ### 物件逃逸示例 **一種典型的物件逃逸就是:物件被複制給成員變數或者靜態變數,可能被外部使用,此時變數就發生了逃逸。** 我們可以用下面的程式碼來表示這個現象。 ```java /** * @author binghe * @description 物件逃逸示例1 */ public class ObjectEscape{ private User user; public void init(){ user = new User(); } } ``` 在ObjectEscape類中,存在一個成員變數user,我們在init()方法中,建立了一個User類的物件,並將其賦值給成員變數user。此時,物件被複制給了成員變數,可能被外部使用,此時的變數就發生了逃逸。 **另一種典型的場景就是:物件通過return語句返回。如果物件通過return語句返回了,此時的程式並不能確定這個物件後續會不會被使用,外部的執行緒可以訪問到這個變數,此時物件也發生了逃逸。** 我們可以用下面的程式碼來表示這個現象。 ```java /** * @author binghe * @description 物件逃逸示例2 */ public class ObjectReturn{ public User createUser(){ User user = new User(); return user; } } ``` 給出兩個示例,相信小夥伴們對JVM的逃逸分析多少有點了解了吧,沒錯,JVM通過逃逸分析,能夠分析出新物件的使用範圍,從而決定新物件是否要在堆上進行分配。 ![](https://img-blog.csdnimg.cn/20200921233635206.jpg#pic_center) 還沒完,我們繼續看下逃逸分析的優點,以便於小夥伴們能夠更好的理解逃逸分析。 ### 逃逸分析的優點 逃逸分析的優點總體上來說可以分為三個:物件可能分配在棧上、分離物件或標量替換、消除同步鎖。我們可以使用下圖來表示。 ![](https://img-blog.csdnimg.cn/20200921233655382.jpg) **物件可能分配在棧上** JVM通過逃逸分析,分析出新物件的使用範圍,就可能將物件在棧上進行分配。棧分配可以快速地在棧幀上建立和銷燬物件,不用再將物件分配到堆空間,可以有效地減少 JVM 垃圾回收的壓力。 **分離物件或標量替換** 當JVM通過逃逸分析,確定要將物件分配到棧上時,即時編譯可以將物件打散,將物件替換為一個個很小的區域性變數,我們將這個打散的過程叫做標量替換。將物件替換為一個個區域性變數後,就可以非常方便的在棧上進行分配了。 **同步鎖消除** 如果JVM通過逃逸分析,發現一個物件只能從一個執行緒被訪問到,則訪問這個物件時,可以不加同步鎖。如果程式中使用了synchronized鎖,則JVM會將synchronized鎖消除。 **這裡,需要注意的是:這種情況針對的是synchronized鎖,而對於Lock鎖,則JVM並不能消除。** 要開啟同步消除,需要加上 -XX:+EliminateLocks 引數。因為這個引數依賴逃逸分析,所以同時要開啟 -XX:+DoEscapeAnalysis 選項。 **所以,並不是所有的物件和陣列,都是在堆上進行分配的,由於即時編譯的存在,如果JVM發現某些物件沒有逃逸出方法,就很有可能被優化成在棧上分配。** ## 重磅福利 微信搜一搜【冰河技術】微信公眾號,關注這個有深度的程式設計師,每天閱讀超硬核技術乾貨,公眾號內回覆【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文件,以及我為大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的工作,學習是一條時而鬱鬱寡歡,時而開懷大笑的路,加油。如果你通過努力成功進入到了心儀的公司,一定不要懈怠放鬆,職場成長和新技術學習一樣,不進則退。如果有幸我們江湖再見! 另外,我開源的各個PDF,後續我都會持續更新和維護,感謝大家長期以來對冰河的支援!! ## 寫在最後 > 如果你覺得冰河寫的還不錯,請微信搜尋並關注「 **冰河技術** 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 **冰河技術** 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 **冰河技術** 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 **冰河技術** 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫! ![](https://img-blog.csdnimg.cn/20200906013715