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). |
這裏的 |
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