TLAB(Thread Local Allocation Buffer)
????線程本地分配緩存石窑,這是一個(gè)線程獨(dú)享的內(nèi)存分配區(qū)域尼斧。
特點(diǎn):
- TLAB解決了:直接在線程共享堆上安全分配帶來的線程同步性能消耗問題(解決了指針碰撞)棺棵。
- TLAB內(nèi)存空間位于Eden區(qū)烛恤。
- 默認(rèn)TLAB大小為占用Eden Space的1%缚柏。
開啟TLAB的參數(shù):
TLAB的數(shù)據(jù)結(jié)構(gòu):
class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
HeapWord* _start; // address of TLAB
HeapWord* _top; // address after last allocation
HeapWord* _pf_top; // allocation prefetch watermark
HeapWord* _end; // allocation end (excluding alignment_reserve)
size_t _desired_size; // desired size (including alignment_reserve)
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
.....................省略......................
}
- _start 指TLAB連續(xù)內(nèi)存起始地址币喧。
- _top 指TLAB當(dāng)前分配到的地址杀餐。
- _end 指TLAB連續(xù)內(nèi)存截止地址史翘。
- _desired_size 是指TLAB的內(nèi)存大小琼讽。
- _refill_waste_limit 是指最大的浪費(fèi)空間钻蹬。默認(rèn)值為64b脉让,jdk1.8<<<<<傳送門
- eg:假設(shè)為_refill_waste_limit=5KB:
????????1、假如當(dāng)前TLAB已經(jīng)分配96KB术唬,還剩下4KB可分配粗仓,但是現(xiàn)在new了一個(gè)對(duì)象需要6KB的空間借浊,顯然TLAB的內(nèi)存不夠了蚂斤,4kb<5kb這時(shí)只浪費(fèi)4KB的空間曙蒸,在_refill_waste_limit 之內(nèi)纽窟,這時(shí)可以申請(qǐng)一個(gè)新的TLAB空間臂港,原先的TLAB交給Eden管理审孽。
????????2、假如當(dāng)前TLAB已經(jīng)分配90KB显拳,還剩下10KB,現(xiàn)在new了一個(gè)對(duì)象需要11KB瘸洛,顯然TLAB的內(nèi)存不夠了反肋,這時(shí)就不能簡單的拋棄當(dāng)前TLAB,這11KB會(huì)被安排到Eden區(qū)進(jìn)行申請(qǐng)畅形。
- eg:假設(shè)為_refill_waste_limit=5KB:
分配規(guī)則:
????????1、obj_size + tlab_top <= tlab_end日熬,直接在TLAB空間分配對(duì)象。
????????2耘纱、obj_size + tlab_top >= tlab_end && tlab_free > tlab_refill_waste_limit毕荐,對(duì)象不在TLAB分配,在Eden區(qū)分配员寇。(tlab_free:剩余的內(nèi)存空間虽填,tlab_refill_waste_limit:允許浪費(fèi)的內(nèi)存空間)<----總結(jié):tlab剩余可用空間>tlab可浪費(fèi)空間,當(dāng)前線程不能丟棄當(dāng)前TLAB斋日,本次申請(qǐng)交由Eden區(qū)分配空間。
????????3第献、????????obj_size + tlab_top >= tlab_end && tlab_free < _refill_waste_limit,重新分配一塊TLAB空間仔拟,在新的TLAB中分配對(duì)象飒赃。<----總結(jié):tlab剩余可用空間<tlab可浪費(fèi)空間,在當(dāng)前允許可浪費(fèi)空間內(nèi)炒事,重新申請(qǐng)一個(gè)新TLAB空間蔫慧,原TLAB交給Eden。
清單:/src/share/vm/memory/ThreadLocalAllocationBuffer.inline.hpp
功能:TLAB內(nèi)存分配
inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) {
invariants();
// 獲取當(dāng)前top
HeapWord* obj = top();
if (pointer_delta(end(), obj) >= size) {
// successful thread-local allocation
#ifdef ASSERT
// Skip mangling the space corresponding to the object header to
// ensure that the returned space is not considered parsable by
// any concurrent GC thread.
size_t hdr_size = oopDesc::header_size();
Copy::fill_to_words(obj + hdr_size, size - hdr_size, badHeapWordVal);
#endif // ASSERT
// This addition is safe because we know that top is
// at least size below end, so the add can't wrap.
// 重置top
set_top(obj + size);
invariants();
return obj;
}
return NULL;
}
??????實(shí)際上虛擬機(jī)內(nèi)部會(huì)維護(hù)一個(gè)叫作refill_waste的值睡扬,當(dāng)請(qǐng)求對(duì)象大于refill_waste時(shí)黍析,會(huì)選擇在堆中分配,若小于該值韧涨,則會(huì)廢棄當(dāng)前TLAB,新建TLAB來分配對(duì)象虑粥。這個(gè)閾值可以使用TLABRefillWasteFraction來調(diào)整宪哩,它表示TLAB中允許產(chǎn)生這種浪費(fèi)的比例。默認(rèn)值為64彬祖,即表示使用約為1/64的TLAB空間作為refill_waste品抽。默認(rèn)情況下,TLAB和refill_waste都會(huì)在運(yùn)行時(shí)不斷調(diào)整的圆恤,使系統(tǒng)的運(yùn)行狀態(tài)達(dá)到最優(yōu)。如果想要禁用自動(dòng)調(diào)整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB焊虏,并使用-XX:TLABSize手工指定一個(gè)TLAB的大小秕磷。
指針碰撞&Eden區(qū)分配
// 指針碰撞分配
HeapWord* compare_to = *Universe::heap()->top_addr();
HeapWord* new_top = compare_to + obj_size;
if (new_top <= *Universe::heap()->end_addr()) {
if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
goto retry;
}
result = (oop) compare_to;
}
}
Eden區(qū)指針碰撞澎嚣,需要模擬多線程并發(fā)申請(qǐng)內(nèi)存空間。且需要關(guān)閉逃逸分析 -XX:-DoEscapeAnalysis -XX:+UseTLAB
/**
* @author biudefu
* @since 2019/8/19 下午11:25
-Xmx100m -Xms100m -XX:-DoEscapeAnalysis -XX:+UseTLAB
-XX:TLABWasteTargetPercent=1 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails
*/
public class AllocationTLABSomeThread {
private static final int threadNum = 100;
private static CountDownLatch latch = new CountDownLatch(threadNum);
private static final int n = 50000000 / threadNum;
private static void alloc() {
byte[] b = new byte[100];
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
for (int j = 0; j < n; j++) {
alloc();
}
latch.countDown();
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
System.out.println("hello world");
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
運(yùn)行結(jié)果:
-XX:-DoEscapeAnalysis -XX:InitialHeapSize=104857600 -XX:MaxHeapSize=104857600 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:TLABWasteTargetPercent=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC -XX:+UseTLAB
[GC (Allocation Failure) [PSYoungGen: 25600K->960K(29696K)] 25600K->968K(98304K), 0.0019559 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 26560K->960K(29696K)] 26568K->968K(98304K), 0.0022243 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 26560K->768K(29696K)] 26568K->776K(98304K), 0.0022446 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
........
[GC (Allocation Failure) [PSYoungGen: 32768K->0K(33280K)] 34193K->1425K(101888K), 0.0014598 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 32768K->0K(33280K)] 34193K->1425K(101888K), 0.0015168 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
823
Heap
PSYoungGen total 33280K, used 3655K [0x00000007bdf00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 32768K, 11% used [0x00000007bdf00000,0x00000007be291c48,0x00000007bff00000)
from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
ParOldGen total 68608K, used 1425K [0x00000007b9c00000, 0x00000007bdf00000, 0x00000007bdf00000)
object space 68608K, 2% used [0x00000007b9c00000,0x00000007b9d64798,0x00000007bdf00000)
Metaspace used 4255K, capacity 4718K, committed 4992K, reserved 1056768K
class space used 477K, capacity 533K, committed 640K, reserved 1048576K
關(guān)閉逃逸和TLAB分配 -XX:-DoEscapeAnalysis -XX:-UseTLAB
運(yùn)行結(jié)果:
-XX:-DoEscapeAnalysis -XX:InitialHeapSize=104857600 -XX:MaxHeapSize=104857600 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:TLABWasteTargetPercent=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC -XX:-UseTLAB
[GC (Allocation Failure) [PSYoungGen: 25599K->976K(29696K)] 25599K->984K(98304K), 0.0023516 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 26575K->880K(29696K)] 26583K->888K(98304K), 0.0015459 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 26480K->832K(29696K)] 26488K->840K(98304K), 0.0006776 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
.......
[GC (Allocation Failure) [PSYoungGen: 32767K->0K(33280K)] 34053K->1285K(101888K), 0.0004838 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 32767K->0K(33280K)] 34053K->1285K(101888K), 0.0005389 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
5388
Heap
PSYoungGen total 33280K, used 21392K [0x00000007bdf00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 32768K, 65% used [0x00000007bdf00000,0x00000007bf3e4230,0x00000007bff00000)
from space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
to space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
ParOldGen total 68608K, used 1285K [0x00000007b9c00000, 0x00000007bdf00000, 0x00000007bdf00000)
object space 68608K, 1% used [0x00000007b9c00000,0x00000007b9d41788,0x00000007bdf00000)
Metaspace used 4248K, capacity 4718K, committed 4992K, reserved 1056768K
class space used 478K, capacity 533K, committed 640K, reserved 1048576K
經(jīng)過對(duì)比颈抚,相差7倍左右嚼鹉。二者內(nèi)存回收??,從YoungGC次數(shù)和耗時(shí)上沒有太大變化:應(yīng)為都是Eden區(qū)分配匹舞。