【性能優化】面試官:Java中的對象都是在堆上分配的嗎?

寫在前面

從開始學習Java的時候,咱們就接觸了這樣一種觀點: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,後續我都會持續更新和維護,感謝你們長期以來對冰河的支持!!

寫在最後

若是你以爲冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習高併發、分佈式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公衆號更新了大量技術專題,每一篇技術文章乾貨滿滿!很多讀者已經經過閱讀「 冰河技術 」微信公衆號文章,吊打面試官,成功跳槽到大廠;也有很多讀者實現了技術上的飛躍,成爲公司的技術骨幹!若是你也想像他們同樣提高本身的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公衆號吧,天天更新超硬核技術乾貨,讓你對如何提高技術能力再也不迷茫!

相關文章
相關標籤/搜索