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

冰河團隊發表於2020-09-21

寫在前面

從開始學習Java的時候,我們就接觸了這樣一種觀點:Java中的物件是在堆上建立的,物件的引用是放在棧裡的,那這個觀點就真的是正確的嗎?如果是正確的,那麼,面試官為啥會問:“Java中的物件就一定是在堆上分配的嗎?”這個問題呢?看來,我們從接觸Java就被灌輸的這個觀點值得我們懷疑。

關於面試題

標題中的面試題為:Java中的物件都是在堆上分配的嗎?

面試官這樣問,有些小夥伴心裡會想:我從一開始學習Java時,就知道了:Java中的物件是在堆上建立的,物件的引用是儲存到棧中的,那Java中的物件是在堆上分配的啊!難道不是嗎?

如果你這樣回答,就會被直接Pass掉。

或許有些小夥伴還是不太明白,那我們繼續往下看。

面試題答案

首先,我們先給出這個題目的答案,這裡我先簡短的回答下這個面試題,後續我們會進行相關分析。

你可以這樣回答:Java中的物件不一定是在堆上分配的,因為JVM通過逃逸分析,能夠分析出一個新物件的使用範圍,並以此確定是否要將這個物件分配到堆上。

這裡,我們接觸了一個新名詞:逃逸分析。相信很多小夥伴不是很明白,那我們繼續往下看。

逃逸分析

逃逸分析的概念

先以官方的形式來說下什麼是逃逸分析。逃逸分析就是:一種確定指標動態範圍的靜態分析,它可以分析在程式的哪些地方可以訪問到指標。

在JVM的即時編譯語境下,逃逸分析將判斷新建的物件是否逃逸。即時編譯判斷物件是否逃逸的依據:一種是物件是否被存入堆中(靜態欄位或者堆中物件的例項欄位),另一種就是物件是否被傳入未知程式碼。

直接說這些概念,確實有點暈啊,那我們就來兩個示例。

物件逃逸示例

一種典型的物件逃逸就是:物件被複制給成員變數或者靜態變數,可能被外部使用,此時變數就發生了逃逸。

我們可以用下面的程式碼來表示這個現象。

/**
 * @author binghe
 * @description 物件逃逸示例1
 */
public class ObjectEscape{
    private User user;
    public void init(){
        user = new User();
    }
}

在ObjectEscape類中,存在一個成員變數user,我們在init()方法中,建立了一個User類的物件,並將其賦值給成員變數user。此時,物件被複制給了成員變數,可能被外部使用,此時的變數就發生了逃逸。

另一種典型的場景就是:物件通過return語句返回。如果物件通過return語句返回了,此時的程式並不能確定這個物件後續會不會被使用,外部的執行緒可以訪問到這個變數,此時物件也發生了逃逸。

我們可以用下面的程式碼來表示這個現象。

/**
 * @author binghe
 * @description 物件逃逸示例2
 */
public class ObjectReturn{
    public User createUser(){
        User user = new User();
        return user;
    }
}

給出兩個示例,相信小夥伴們對JVM的逃逸分析多少有點了解了吧,沒錯,JVM通過逃逸分析,能夠分析出新物件的使用範圍,從而決定新物件是否要在堆上進行分配。

還沒完,我們繼續看下逃逸分析的優點,以便於小夥伴們能夠更好的理解逃逸分析。

逃逸分析的優點

逃逸分析的優點總體上來說可以分為三個:物件可能分配在棧上、分離物件或標量替換、消除同步鎖。我們可以使用下圖來表示。

物件可能分配在棧上

JVM通過逃逸分析,分析出新物件的使用範圍,就可能將物件在棧上進行分配。棧分配可以快速地在棧幀上建立和銷燬物件,不用再將物件分配到堆空間,可以有效地減少 JVM 垃圾回收的壓力。

分離物件或標量替換

當JVM通過逃逸分析,確定要將物件分配到棧上時,即時編譯可以將物件打散,將物件替換為一個個很小的區域性變數,我們將這個打散的過程叫做標量替換。將物件替換為一個個區域性變數後,就可以非常方便的在棧上進行分配了。

同步鎖消除

如果JVM通過逃逸分析,發現一個物件只能從一個執行緒被訪問到,則訪問這個物件時,可以不加同步鎖。如果程式中使用了synchronized鎖,則JVM會將synchronized鎖消除。

這裡,需要注意的是:這種情況針對的是synchronized鎖,而對於Lock鎖,則JVM並不能消除。

要開啟同步消除,需要加上 -XX:+EliminateLocks 引數。因為這個引數依賴逃逸分析,所以同時要開啟 -XX:+DoEscapeAnalysis 選項。

所以,並不是所有的物件和陣列,都是在堆上進行分配的,由於即時編譯的存在,如果JVM發現某些物件沒有逃逸出方法,就很有可能被優化成在棧上分配。

重磅福利

微信搜一搜【冰河技術】微信公眾號,關注這個有深度的程式設計師,每天閱讀超硬核技術乾貨,公眾號內回覆【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文件,以及我為大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的工作,學習是一條時而鬱鬱寡歡,時而開懷大笑的路,加油。如果你通過努力成功進入到了心儀的公司,一定不要懈怠放鬆,職場成長和新技術學習一樣,不進則退。如果有幸我們江湖再見!

另外,我開源的各個PDF,後續我都會持續更新和維護,感謝大家長期以來對冰河的支援!!

寫在最後

如果你覺得冰河寫的還不錯,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!

相關文章