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

Java對象分配流程java

棧上分配技術:

是java虛擬機提供的一項優化技術,它的基本思想是,對於那些線程私有對象(指不可能被其餘線程訪問的對象)能夠將它們打散分配在棧上,而不是分配在堆上。緩存

好處: 分配在棧上能夠結束後自行銷燬,不須要垃圾回收器介入,從而提升系統的性能。性能優化

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

基礎:棧上分配依賴於逃逸分析和標量替換。markdown

逃逸分析:

棧上分配的一個技術基礎是進行逃逸分析。目的是判斷對象的做用域是否有可能逃逸出逃逸體。多線程

虛擬機會進行逃逸分析,判斷線程內私有對象是否有可能被其餘線程訪問,致使逃逸,而後虛擬機就會根據是否可能會逃逸將其分配在棧上,或者堆中。函數

只有在server模式下,才能開啓逃逸分析。 以下示例: oop

參數: -XX:+DoEscapeAnalysis 是開啓逃逸分析。 -XX:+EliminateAllocations 是開啓標杆替換,容許將對象打散分配到棧上,默認就是打開的。

代碼:性能

//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");
}
複製代碼

棧上示例分配:<來自實戰java虛擬機>優化

public class AllotOnStack {

   public static class{
       public int id=0;
       public String name="";
   }

   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("zengxinyao");
   }
}
複製代碼

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

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

-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations
複製代碼

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

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

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

能夠得出: 棧上分配依賴於逃逸分析和標量替換

JVM參數解析

TLAB 分配

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

爲何須要TLAB?

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

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

分配策略:

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

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

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

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

取自: 《實戰Java虛擬機 - JVM故障診斷與性能優化》 棧上分配、TLAB : blog.csdn.net/yangsnow_ra…

相關文章
相關標籤/搜索