JVM之對象分配:棧上分配 & TLAB分配

1. Java對象分配流程緩存

    

2. 棧上分配性能優化

    2.1 本質:Java虛擬機提供的一項優化技術數據結構

    2.2 基本思想: 將線程私有的對象打散分配多線程

    2.3 優勢: 函數

       2.3.1 能夠在函數調用結束後自行銷燬對象,不須要垃圾回收器的介入,有效避免垃圾回收帶來的負面影響性能

       2.3.2 棧上分配速度快,提升系統性能優化

    2.4 侷限性: 棧空間小,對於大對象沒法實現棧上分配this

    2.4 技術基礎: 逃逸分析spa

        2.4.1 逃逸分析的目的: 判斷對象的做用域是否超出函數體[即:判斷是否逃逸出函數體].net

//user的做用域超出了函數setUser的範圍,是逃逸對象
//當函數結束調用時,不會自行銷燬user
private User user;
public void setUser(){
    user = new User();
    user.setId(1);
    user.setName("blueStarWei");
}

//u只在函數內部生效,不是逃逸對象
//當函數調用結束,會自行銷燬對象u
public void createUser(){
    User u = new User();
    u.setId(2);
    u.setName("JVM");
}

    2.5 棧上分配示例

package com.blueStarWei.templet;

public class AllotOnStack {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    private static void alloc() {
        User user = new User();
        user.setId(1);
        user.setName("blueStarWei");
    }
}

        2.5.1 上述代碼調用了1億次alloc(),若是是分配到堆上,大概須要1.5GB的堆空間,若是堆空間小於該值,必然會觸發GC。

        2.5.2 使用以下參數運行,發現不會觸發GC

-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

       2.5.3 使用以下參數(任意一行)運行,會發現觸大量GC

//不使用逃逸分析
-server -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

//不使用標量替換
-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:-EliminateAllocations

        2.5.3.1 能夠發現:棧上分配依賴於逃逸分析標量替換 

    2.5.4 GC日誌

[GC (Allocation Failure)  4095K->528K(15872K), 0.0025208 secs]
[GC (Allocation Failure)  4624K->552K(15872K), 0.0012518 secs]
[GC (Allocation Failure)  4648K->608K(15872K), 0.0009262 secs]
......(省略)
3718

        2.5.4.1 GC日誌解析

參數

做用

備註

   GC

 用來區分是 Minor GC 仍是 Full GC 的標誌(Flag). 

這裏的 GC 代表本次發生的是 Minor GC.

Allocation Failure
引發垃圾回收的緣由.  本次GC是由於年輕代中沒有任何合適的區域可以存放須要分配的數據結構而觸發的.
4095K->528K
 在本次GC以前和以後的年輕代內存使用狀況.  本次GC前,年輕代使用空間4095K, GC後年輕代使用空間爲528K
(15872K)
 年輕代的總的大小  
0.0025208 secs
 本次GC使用時間(單位:秒)  

      2.5.5 JVM參數解析

參數 做用 備註
-server
使用server模式 只有在server模式下,才能夠棄用逃逸分析
-Xmx15m
設置最大堆空間爲15m 若是在堆上分配,必然觸發大量GC
-Xms15m
設初始對空間爲15m  
-XX:+DoEscapeAnalysis
啓用逃逸分析 默認啓用
-XX:-DoEscapeAnalysis
關閉逃逸分析  
-XX:+PrintGC
打印GC日誌  
   -XX:-UseTLAB  關閉TLAB

 TLAB(Thread Local Allocation Buffer)

線程本地分配緩存區

-XX:+EliminateAllocations
啓用標量替換,容許對象打散分配到棧上

默認啓用

-XX:-EliminateAllocations
 關閉標量替換  

 

3. TLAB 分配

  TLAB,全稱Thread Local Allocation Buffer, 即:線程本地分配緩存。這是一塊線程專用的內存分配區域TLAB佔用的是eden區的空間。在TLAB啓用的狀況下(默認開啓),JVM會爲每個線程分配一塊TLAB區域。

    3.1 爲何須要TLAB?

  這是爲了加速對象的分配。因爲對象通常分配在堆上,而堆是線程共用的,所以可能會有多個線程在堆上申請空間,而每一次的對象分配都必須線程同步,會使分配的效率降低。考慮到對象分配幾乎是Java中最經常使用的操做,所以JVM使用了TLAB這樣的線程專有區域來避免多線程衝突,提升對象分配的效率。

    3.2 侷限性: TLAB空間通常不會太大(佔用eden區),因此大對象沒法進行TLAB分配,只能直接分配到堆上。

    3.3 分配策略:

  一個100KB的TLAB區域,若是已經使用了80KB,當須要分配一個30KB的對象時,TLAB是如何分配的呢?

    此時,虛擬機有兩種選擇:第一,廢棄當前的TLAB(會浪費20KB的空3.4 間);第二,將這個30KB的對象直接分配到堆上,保留當前TLAB(當有小於20KB的對象請求TLAB分配時能夠直接使用該TLAB區域)。

  JVM選擇的策略是:在虛擬機內部維護一個叫refill_waste的值,當請求對象大於refill_waste時,會選擇在堆中分配,反之,則會廢棄當前TLAB,新建TLAB來分配新對象。

  【默認狀況下,TLAB和refill_waste都是會在運行時不斷調整的,使系統的運行狀態達到最優。】

    3.4 JVM參數解析

參數 做用 備註
-XX:+UseTLAB 啓用TLAB 默認啓用
-XX:TLABRefillWasteFraction 設置容許空間浪費的比例 默認值:64,即:使用1/64的TLAB空間大小做爲refill_waste值
-XX:-ResizeTLAB 禁止系統自動調整TLAB大小  
-XX:TLABSize 指定TLAB大小 單位:B


4. 附件 

    4.1 User類

packagepackag  com.blueStarWei.templet;

public class User {

    private int id;
    
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}
 

 

5. 參考文獻

    5.1 《實戰Java虛擬機 - JVM故障診斷與性能優化》

    5.2  棧上分配、TLAB : https://blog.csdn.net/yangsnow_rain_wind/article/details/80434323

    5.3  快速解讀GC日誌 : https://blog.csdn.net/renfufei/article/details/49230943

相關文章
相關標籤/搜索