天天看點

JVM——棧配置設定與TLAB

前言

在學習Java的過程中,一般認為new出來的對象都是被配置設定在堆上的,其實這個結論不完全正确,因為是大部分new出來的對象被配置設定在堆上,而不是全部。通過對Java對象配置設定的過程分析,可以知道有另外兩個地方也是可以存放對象的。這兩個地方分别為棧 (涉及逃逸分析相關知識)和TLAB(Thread Local Allocation Buffer)。我們首先對這兩者進行介紹,而後對Java對象配置設定過程進行介紹。

棧上配置設定

在JVM中,堆是線程共享的,是以堆上的對象對于各個線程都是共享和可見的,隻要持有對象的引用,就可以通路堆中存儲的對象資料。虛拟機的垃圾收集系統可以回收堆中不再使用的對象,但對于垃圾收集器來說,無論篩選可回收對象,還是回收和整理記憶體都需要耗費時間。

如果确定一個對象的作用域不會逃逸出方法之外,那可以将這個對象配置設定在棧上,這樣,對象所占用的記憶體空間就可以随棧幀出棧而銷毀。在一般應用中,不會逃逸的局部對象所占的比例很大,如果能使用棧上配置設定,那大量的對象就會随着方法的結束而自動銷毀了,無須通過垃圾收集器回收,可以減小垃圾收集器的負載。

JVM允許将線程私有的對象打散配置設定在棧上,而不是配置設定在堆上。配置設定在棧上的好處是可以在函數調用結束後自行銷毀,而不需要垃圾回收器的介入,進而提高系統性能。

棧上配置設定的技術基礎:

  • 1、逃逸分析:逃逸分析的目的是判斷對象的作用域是否有可能逃逸出函數體。
  • 2、标量替換:允許将對象打散配置設定在棧上,比如若一個對象擁有兩個字段,會将這兩個字段視作局部變量進行配置設定。

隻能在server模式下才能啟用逃逸分析,參數-XX:DoEscapeAnalysis啟用逃逸分析,參數-XX:+EliminateAllocations開啟标量替換(預設打開)。Java SE 6u23版本之後,HotSpot中預設就開啟了逃逸分析,可以通過選項-XX:+PrintEscapeAnalysis檢視逃逸分析的篩選結果。

TLAB

TLAB的全稱是Thread Local Allocation Buffer,即線程本地配置設定緩存區,這是一個線程專用的記憶體配置設定區域。

由于對象一般會配置設定在堆上,而堆是全局共享的。是以在同一時間,可能會有多個線程在堆上申請空間。是以,每次對象配置設定都必須要進行同步(虛拟機采用CAS配上失敗重試的方式保證更新操作的原子性),而在競争激烈的場合配置設定的效率又會進一步下降。JVM使用TLAB來避免多線程沖突,在給對象配置設定記憶體時,每個線程使用自己的TLAB,這樣可以避免線程同步,提高了對象配置設定的效率。

TLAB本身占用eEden區空間,在開啟TLAB的情況下,虛拟機會為每個Java線程配置設定一塊TLAB空間。參數-XX:+UseTLAB開啟TLAB,預設是開啟的。TLAB空間的記憶體非常小,預設情況下僅占有整個Eden空間的1%,當然可以通過選項-XX:TLABWasteTargetPercent設定TLAB空間所占用Eden空間的百分比大小。

由于TLAB空間一般不會很大,是以大對象無法在TLAB上進行配置設定,總是會直接配置設定在堆上。TLAB空間由于比較小,是以很容易裝滿。比如,一個100K的空間,已經使用了80KB,當需要再配置設定一個30KB的對象時,肯定就無能為力了。這時虛拟機會有兩種選擇,第一,廢棄目前TLAB,這樣就會浪費20KB空間;第二,将這30KB的對象直接配置設定在堆上,保留目前的TLAB,這樣可以希望将來有小于20KB的對象配置設定請求可以直接使用這塊空間。實際上虛拟機内部會維護一個叫作refill_waste的值,當請求對象大于refill_waste時,會選擇在堆中配置設定,若小于該值,則會廢棄目前TLAB,建立TLAB來配置設定對象。這個門檻值可以使用TLABRefillWasteFraction來調整,它表示TLAB中允許産生這種浪費的比例。預設值為64,即表示使用約為1/64的TLAB空間作為refill_waste。預設情況下,TLAB和refill_waste都會在運作時不斷調整的,使系統的運作狀态達到最優。如果想要禁用自動調整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB,并使用-XX:TLABSize手工指定一個TLAB的大小。

-XX:+PrintTLAB可以跟蹤TLAB的使用情況。一般不建議手工修改TLAB相關參數,推薦使用虛拟機預設行為。

對象記憶體配置設定的兩種方法

為對象配置設定空間的任務等同于把一塊确定大小的記憶體從Java堆中劃分出來。

指針碰撞(Serial、ParNew等帶Compact過程的收集器)

空閑清單(CMS這種基于Mark-Sweep算法的收集器)