天天看點

JVM 對象配置設定過程

JVM 對象配置設定過程

對象配置設定過程

1)依據逃逸分析,判斷是否能棧上配置設定? 如果可以,使用标量替換方式,把對象配置設定到vm stack中。如果 線程銷毀或方法調用結束後,自動銷毀,不需要 gc 回收器 介入。否則,繼續下一步。

2)判斷是否大對象? 如果是,直接配置設定到堆上 old generation 老年代上。如果對象變為垃圾後,由老年代gc 收集器(比如 parallel old, cms, g1)回收。否則,繼續下一步。

3)判斷是否可以在 tlab中配置設定? 如果是,在 tlab中配置設定堆上eden區。否則,在 tlab外堆上的eden區配置設定。

棧上配置設定

本質上是jvm提供的一個優化技術。

基本思想:将線程私有的對象打散配置設定在棧 vm stack上

優點: 可以在函數調用結束後自行銷毀對象,不需要垃圾回收器的介入,有效避免垃圾回收帶來的負面影響棧上配置設定速度快,提高系統性能

局限性: 棧空間小,對于大對象無法實作棧上配置設定

技術基礎: 逃逸分析、标量替換

什麼是逃逸分析?

關于 java 逃逸分析的定義:

逃逸分析(escape analysis)簡單來講就是,java hotspot 虛拟機可以分析新建立對象的使用範圍,并決定是否在 java 堆上配置設定記憶體的一項技術。

逃逸分析的 jvm 參數如下:

開啟逃逸分析:-xx:+doescapeanalysis

關閉逃逸分析:-xx:-doescapeanalysis

顯示分析結果:-xx:+printescapeanalysis

逃逸分析技術在 java se 6u23+ 開始支援,并預設設定為啟用狀态,可以不用額外加這個參數。

逃逸分析優化

針對上面第三點,當一個對象沒有逃逸時,可以得到以下幾個虛拟機的優化。

1) 鎖消除

我們知道線程同步鎖是非常犧牲性能的,當編譯器确定目前對象隻有目前線程使用,那麼就會移除該對象的同步鎖。

例如,stringbuffer 和 vector 都是用 synchronized 修飾線程安全的,但大部分情況下,它們都隻是在目前線程中用到,這樣編譯器就會優化移除掉這些鎖操作。

鎖消除的 jvm 參數如下:

開啟鎖消除:-xx:+eliminatelocks

關閉鎖消除:-xx:-eliminatelocks

鎖消除在 jdk8 中都是預設開啟的,并且鎖消除都要建立在逃逸分析的基礎上。

2) 标量替換

首先要明白标量和聚合量,基礎類型和對象的引用可以了解為标量,它們不能被進一步分解。而能被進一步分解的量就是聚合量,比如:對象。

對象是聚合量,它又可以被進一步分解成标量,将其成員變量分解為分散的變量,這就叫做标量替換。

這樣,如果一個對象沒有發生逃逸,那壓根就不用建立它,隻會在棧或者寄存器上建立它用到的成員标量,節省了記憶體空間,也提升了應用程式性能。

标量替換的 jvm 參數如下:

開啟标量替換:-xx:+eliminateallocations

關閉标量替換:-xx:-eliminateallocations

顯示标量替換詳情:-xx:+printeliminateallocations

标量替換同樣在 jdk8 中都是預設開啟的,并且都要建立在逃逸分析的基礎上。

3) 棧上配置設定

當對象沒有發生逃逸時,該對象就可以通過标量替換分解成成員标量配置設定在棧記憶體中,和方法的生命周期一緻,随着棧幀出棧時銷毀,減少了 gc 壓力,提高了應用程式性能。

示例代碼

上述代碼調用了1億次alloc(),如果是配置設定到堆上,大概需要 2.2 gb的堆空間,如果堆空間小于該值,必然會觸發gc。

使用如下vm參數運作,發現不會觸發gc:

JVM 對象配置設定過程

使用如下參數(任意一行)運作,會發現觸大量 gc:

JVM 對象配置設定過程

tlab 配置設定

tlab,全稱thread local allocation buffer, 即:線程本地配置設定緩存。這是一塊線程專用的記憶體配置設定區域。

tlab占用的是eden區的空間。

在tlab啟用的情況下(預設開啟),jvm會為每一個線程配置設定一塊tlab區域。

為什麼需要tlab?

這是為了加速對象的配置設定。

由于對象一般配置設定在堆上,而堆是線程共用的,是以可能會有多個線程在堆上申請空間,而每一次的對象配置設定都必須線程同步,會使配置設定的效率下降。

考慮到對象配置設定幾乎是java中最常用的操作,是以jvm使用了tlab這樣的線程專有區域來避免多線程沖突,提高對象配置設定的效率。

局限性: tlab空間一般不會太大(占用eden區),是以大對象無法進行tlab配置設定,隻能直接配置設定到堆 heap上。

大對象

大對象的 jvm 參數如下:

大對象到底多大:-xx:pretenuresizethreshold=n (僅适用于 defnew / parnew新生代垃圾回收器 ) https://bugs.openjdk.java.net/browse/jdk-8050209

g1回收器的大對象判斷,則依據region的大小(-xx:g1heapregionsize)來判斷,如果對象大于region50%以上,就判斷為大對象humongous object。