Java對象分配流程
棧上分配技術:
是java虛擬機提供的一項優(yōu)化技術,它的基本思想是,對于那些線程私有對象(指不可能被其他線程訪問的對象)可以將它們打散分配在棧上鹰溜,而不是分配在堆上序芦。
好處: 分配在棧上可以結束后自行銷毀,不需要垃圾回收器介入蔬咬,從而提高系統(tǒng)的性能鲤遥。
局限性: 棧空間小林艘,對于大對象無法實現(xiàn)棧上分配盖奈。
基礎:棧上分配依賴于逃逸分析和標量替換。
逃逸分析:
棧上分配的一個技術基礎是進行逃逸分析北启。目的是判斷對象的作用域是否有可能逃逸出逃逸體卜朗。
虛擬機會進行逃逸分析,判斷線程內(nèi)私有對象是否有可能被其他線程訪問咕村,導致逃逸场钉,然后虛擬機就會根據(jù)是否可能會逃逸將其分配在棧上,或者堆中懈涛。
只有在server模式下逛万,才能開啟逃逸分析。
如下示例:
參數(shù):
-XX:+DoEscapeAnalysis 是開啟逃逸分析。
-XX:+EliminateAllocations 是開啟標桿替換宇植,允許將對象打散分配到棧上得封,默認就是打開的。
代碼:
//user的作用域超出了函數(shù)setUser的范圍,是逃逸對象
//當函數(shù)結束調(diào)用時指郁,不會自行銷毀user
private User user;
public void setUser(){
user = new User();
user.setId(1);
user.setName("blueStarWei");
}
//u只在函數(shù)內(nèi)部生效忙上,不是逃逸對象
//當函數(shù)調(diào)用結束,會自行銷毀對象u
public void createUser(){
User u = new User();
u.setId(2);
u.setName("JVM");
}
棧上示例分配:<來自實戰(zhàn)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");
}
}
上述代碼調(diào)用了1億次alloc()闲坎,如果是分配到堆上疫粥,大概需要1.5GB的堆空間,如果堆空間小于該值腰懂,必然會觸發(fā)GC梗逮。
使用如下參數(shù)運行,發(fā)現(xiàn)不會觸發(fā)GC
-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations
使用如下參數(shù)(任意一行)運行绣溜,會發(fā)現(xiàn)觸大量GC
//不使用逃逸分析
-server -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations
//不使用標量替換
-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:-EliminateAllocations
可以得出:
棧上分配依賴于逃逸分析和標量替換
JVM參數(shù)解析
TLAB 分配
TLAB慷彤,全稱Thread Local Allocation Buffer,即:線程本地分配緩存。這是一塊線程專用的內(nèi)存分配區(qū)域怖喻。TLAB占用的是eden區(qū)的空間底哗。在TLAB啟用的情況下(默認開啟),JVM會為每一個線程分配一塊TLAB區(qū)域罢防。
為什么需要TLAB艘虎?
這是為了加速對象的分配。由于對象一般分配在堆上咒吐,而堆是線程共用的野建,因此可能會有多個線程在堆上申請空間,而每一次的對象分配都必須線程同步恬叹,會使分配的效率下降候生。考慮到對象分配幾乎是Java中最常用的操作绽昼,因此JVM使用了TLAB這樣的線程專有區(qū)域來避免多線程沖突唯鸭,提高對象分配的效率
局限性:TLAB空間一般不會太大(占用eden區(qū)),所以大對象無法進行TLAB分配硅确,只能直接分配到堆上.
分配策略:
一個100KB的TLAB區(qū)域目溉,如果已經(jīng)使用了80KB,當需要分配一個30KB的對象時菱农,TLAB是如何分配的呢缭付?
此時,虛擬機有兩種選擇:第一循未,廢棄當前的TLAB(會浪費20KB的空3.4 間)嘲叔;第二最仑,將這個30KB的對象直接分配到堆上姨拥,保留當前TLAB(當有小于20KB的對象請求TLAB分配時可以直接使用該TLAB區(qū)域)。
JVM選擇的策略是:在虛擬機內(nèi)部維護一個叫refill_waste的值足陨,當請求對象大于refill_waste時,會選擇在堆中分配娇未,反之墨缘,則會廢棄當前TLAB,新建TLAB來分配新對象忘蟹。
【默認情況下飒房,TLAB和refill_waste都是會在運行時不斷調(diào)整的,使系統(tǒng)的運行狀態(tài)達到最優(yōu)媚值。
取自:
《實戰(zhàn)Java虛擬機 - JVM故障診斷與性能優(yōu)化》
棧上分配、TLAB : https://blog.csdn.net/yangsnow_rain_wind/article/details/80434323