JVM學(xué)習(xí)筆記與調(diào)優(yōu)實(shí)戰(zhàn)(三):Java對象內(nèi)存分配與逃逸分析

標(biāo)簽: JVM


1、Java對象的分配:

  • 棧上分配
    • 線程私有小對象
    • 無逃逸
    • 支持標(biāo)量替換
    • 無需調(diào)整(虛擬機(jī)自動優(yōu)化,無需調(diào)優(yōu))

  • 線程本地分配TLAB(Thread Local Allocation Buffer)
    • 占用eden,默認(rèn)1%,仍在堆上申請,用作線程專用
    • 多線程的時(shí)候不用競爭(加鎖)eden就可以申請空間(同步消除),提高效率
    • 小對象
    • 無需調(diào)整

  • 老年代
    • 大對象(大數(shù)組、長字符串)

  • eden
    • new普通對象

分配策略:
如果JVM啟動了逃逸分析,那么new一個對象時(shí),首先會嘗試在棧上分配,如果分配不了,則會嘗試在線程本地分配,如果棧上分配與線程本地分配均分配失敗的話,則會先判斷該對象是否為大對象,如果是大對象,則在老年代分配內(nèi)存,否則到新生代的eden區(qū)分配。


2、逃逸分析:
逃逸分析是一種為其他優(yōu)化手段提供依據(jù)的分析技術(shù),其基本行為是分析對象動態(tài)作用域:當(dāng)一個對象在方法中被定義后,它可能被外部方法所引用,例如作為調(diào)用參數(shù)傳遞到其他方法中,稱為方法逃逸;也有可能被其外部線程訪問到,如復(fù)制給類變量或者可以在其他線程中訪問的實(shí)例變量,稱為線程逃逸。
如果一個對象不會逃逸到方法或者線程之外,則可以對這個對象進(jìn)行一些高效的優(yōu)化:

  • 棧上分配Stack Allocation:如果一個對象不會逃逸到方法之外,那么可以讓這個對象在棧上分配內(nèi)存,以提高執(zhí)行效率,對象所占內(nèi)存會隨著棧幀出棧而銷毀。在一般應(yīng)用中,無逃逸的局部變量對象所占的比例較大,如果能使用棧上分配,那么大量的對象就會隨著方法的結(jié)束而自動銷毀,GC壓力減小很多。

  • 同步消除SynchronizationElimination:線程同步是一個相對耗時(shí)的過程,如果逃逸分析能夠確定一個變量不會逃逸出線程,無法被其他線程訪問,那么該變量的讀寫不存在競爭關(guān)系,即可以消除掉對這個變量的同步措施

  • 標(biāo)量替換:

    • 標(biāo)量:指的是一個數(shù)據(jù)已經(jīng)無法再分解成更小的數(shù)據(jù)來表示了,Java虛擬機(jī)的原始數(shù)據(jù)類型(int,float等數(shù)值類型以及reference類型)都不能再進(jìn)行進(jìn)一步的分解
    • 聚合量:相對于標(biāo)量,如果一個數(shù)據(jù)可繼續(xù)分解,則可以稱作聚合量,Java對象是典型的聚合量。
    • 如果把一個Java對象拆散,根據(jù)程序訪問的情況,將其使用到的成員變量恢復(fù)原始類型來訪問,這過程成為標(biāo)量替換
    • 如果逃逸分析可以確定一個對象不會被外部訪問,且這個對象可以被拆散,那程序真正執(zhí)行的時(shí)候,可以不創(chuàng)建這個對象,而是直接創(chuàng)建它的成員變量來替換這個對象。將對象拆分后,可以在棧上分配內(nèi)存

3、測試實(shí)例:
參考代碼

package com.vechace.JVM;

/**
* Description:新建10000000個對象,計(jì)算執(zhí)行時(shí)間,再配置不同JVM參數(shù)
* 比較執(zhí)行結(jié)果
* @author vechace
*    -XX:-DoEscapeAnalysis  關(guān)閉逃逸分析
*    -XX:-EliminateAllocations 關(guān)閉標(biāo)量替換
*    -XX:-UseTLAB 關(guān)閉線程本地內(nèi)存
*    -XX:-PrintGC 打印GC信息
*/
public class JVMTest1 {
    
    class User{
        int id;
        String name;
        
        User(int id,String name){
            this.id = id;
            this.name = name;
            
        }
    }
    
    void alloc(int i){
        new User(i,"name"+i);
    }

    public static void main(String[] args) {
        
            JVMTest1 t = new JVMTest1();
            long s1 = System.currentTimeMillis();
            for(int i = 0;i<10000000;i++){
                t.alloc(i);
            }
            long s2 = System.currentTimeMillis();
            System.out.println(s2-s1);

    }

}

IDE:Eclipse

run As --> run configuration --> Argument --> VM argument :填入如下配置:

-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB -XX:+PrintGC

結(jié)果分析:

a.無逃逸分析、無棧上分配、不使用線程本地內(nèi)存:

-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB -XX:+PrintGC

控制臺輸出:

[GC (Allocation Failure)  49152K->688K(188416K), 0.0010012 secs]
[GC (Allocation Failure)  49840K->728K(188416K), 0.0009848 secs]
[GC (Allocation Failure)  49880K->640K(188416K), 0.0007432 secs]
[GC (Allocation Failure)  49792K->672K(237568K), 0.0008412 secs]
[GC (Allocation Failure)  98976K->640K(237568K), 0.0012708 secs]
[GC (Allocation Failure)  98944K->656K(328704K), 0.0008696 secs]
[GC (Allocation Failure)  197264K->624K(328704K), 0.0017397 secs]
[GC (Allocation Failure)  197232K->624K(320512K), 0.0003312 secs]
791

b.使用線程本地內(nèi)存,無需在eden區(qū)分配內(nèi)存時(shí)加鎖,效率變高

-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+UseTLAB -XX:+PrintGC

控制臺輸出:

[GC (Allocation Failure)  49760K->640K(188416K), 0.0007129 secs]
[GC (Allocation Failure)  49792K->624K(237568K), 0.0008062 secs]
[GC (Allocation Failure)  98928K->608K(237568K), 0.0014966 secs]
[GC (Allocation Failure)  98912K->728K(328704K), 0.0008608 secs]
[GC (Allocation Failure)  197336K->588K(328704K), 0.0016310 secs]
[GC (Allocation Failure)  197196K->620K(525312K), 0.0003275 secs]
528

c.開啟逃逸分析、使用標(biāo)量替換、使用線程本地內(nèi)存、效率變高

-XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+UseTLAB -XX:+PrintGC

控制臺輸出:

[GC (Allocation Failure)  49152K->688K(188416K), 0.0010576 secs]
[GC (Allocation Failure)  49840K->640K(188416K), 0.0009443 secs]
[GC (Allocation Failure)  49792K->640K(188416K), 0.0007502 secs]
[GC (Allocation Failure)  49792K->696K(237568K), 0.0008981 secs]
[GC (Allocation Failure)  99000K->656K(237568K), 0.0011229 secs]
[GC (Allocation Failure)  98960K->608K(328704K), 0.0010558 secs]
[GC (Allocation Failure)  197216K->644K(328704K), 0.0015396 secs]
486

問題分析:開啟逃逸分析存在開銷,有時(shí)效率不如未開逃逸分析時(shí)的效率高

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容