9.3.1. TLAB 快分配
src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) { //验证各个内存指针有效,也就是 _top 在 _start 和 _end 范围内 invariants(); HeapWord* obj = top(); //如果空间足够,则分配内存 if (pointer_delta(end(), obj) >= size) { set_top(obj + size); invariants(); return obj; } return NULL; }
9.3.2. TLAB 慢分配
src/hotspot/share/gc/shared/memAllocator.cpp
HeapWord* MemAllocator::allocate_inside_tlab_slow(Allocation& allocation) const { HeapWord* mem = NULL; ThreadLocalAllocBuffer& tlab = _thread->tlab(); // 如果 TLAB 剩余空间大于 最大浪费空间,则记录并让最大浪费空间递增 if (tlab.free() > tlab.refill_waste_limit()) { tlab.record_slow_allocation(_word_size); return NULL; } //重新计算 TLAB 大小 size_t new_tlab_size = tlab.compute_size(_word_size); //TLAB 放回 Eden 区 tlab.retire_before_allocation(); if (new_tlab_size == 0) { return NULL; } // 计算最小大小 size_t min_tlab_size = ThreadLocalAllocBuffer::compute_min_size(_word_size); //分配新的 TLAB 空间,并在里面分配对象 mem = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &allocation._allocated_tlab_size); if (mem == NULL) { assert(allocation._allocated_tlab_size == 0, "Allocation failed, but actual size was updated. min: " SIZE_FORMAT ", desired: " SIZE_FORMAT ", actual: " SIZE_FORMAT, min_tlab_size, new_tlab_size, allocation._allocated_tlab_size); return NULL; } assert(allocation._allocated_tlab_size != 0, "Allocation succeeded but actual size not updated. mem at: " PTR_FORMAT " min: " SIZE_FORMAT ", desired: " SIZE_FORMAT, p2i(mem), min_tlab_size, new_tlab_size); //如果启用了 ZeroTLAB 这个 JVM 参数,则将对象所有字段置零值 if (ZeroTLAB) { // ..and clear it. Copy::zero_to_words(mem, allocation._allocated_tlab_size); } else { // ...and zap just allocated object. } //设置新的 TLAB 空间为当前线程的 TLAB tlab.fill(mem, mem + _word_size, allocation._allocated_tlab_size); //返回分配的对象内存地址 return mem; }
9.3.2.1 TLAB最大浪费空间
TLAB最大浪费空间 _refill_waste_limit
初始值为 TLAB 大小除以 TLABRefillWasteFraction:src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
size_t initial_refill_waste_limit() { return desired_size() / TLABRefillWasteFraction; }
每次慢分配,调用record_slow_allocation(size_t obj_size)
记录慢分配的同时,增加 TLAB 最大浪费空间的大小:
src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) { //每次慢分配,_refill_waste_limit 增加 refill_waste_limit_increment,也就是 TLABWasteIncrement set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment()); _slow_allocations++; log_develop_trace(gc, tlab)("TLAB: %s thread: " INTPTR_FORMAT " [id: %2d]" " obj: " SIZE_FORMAT " free: " SIZE_FORMAT " waste: " SIZE_FORMAT, "slow", p2i(thread()), thread()->osthread()->thread_id(), obj_size, free(), refill_waste_limit()); } //refill_waste_limit_increment 就是 JVM 参数 TLABWasteIncrement static size_t refill_waste_limit_increment() { return TLABWasteIncrement; }
9.3.2.2. 重新计算 TLAB 大小
重新计算会取 当前堆剩余给 TLAB 可分配的空间 和 TLAB 期望大小 + 当前需要分配的空间大小 中的小的那个:
src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
inline size_t ThreadLocalAllocBuffer::compute_size(size_t obj_size) { //获取当前堆剩余给 TLAB 可分配的空间 const size_t available_size = Universe::heap()->unsafe_max_tlab_alloc(thread()) / HeapWordSize; //取 TLAB 可分配的空间 和 TLAB 期望大小 + 当前需要分配的空间大小 以及 TLAB 最大大小中的小的那个 size_t new_tlab_size = MIN3(available_size, desired_size() + align_object_size(obj_size), max_size()); // 确保大小大于 dummy obj 对象头 if (new_tlab_size < compute_min_size(obj_size)) { log_trace(gc, tlab)("ThreadLocalAllocBuffer::compute_size(" SIZE_FORMAT ") returns failure", obj_size); return 0; } log_trace(gc, tlab)("ThreadLocalAllocBuffer::compute_size(" SIZE_FORMAT ") returns " SIZE_FORMAT, obj_size, new_tlab_size); return new_tlab_size; }
9.3.2.3. 当前 TLAB 放回堆
src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
//在TLAB慢分配被调用,当前 TLAB 放回堆 void ThreadLocalAllocBuffer::retire_before_allocation() { //将当前 TLAB 剩余空间大小加入慢分配浪费空间大小 _slow_refill_waste += (unsigned int)remaining(); //执行 TLAB 退还给堆,这个在后面 GC 的时候还会被调用用于将所有的线程的 TLAB 退回堆 retire(); } //对于 TLAB 慢分配,stats 为空 //对于 GC 的时候调用,stats 用于记录每个线程的数据 void ThreadLocalAllocBuffer::retire(ThreadLocalAllocStats* stats) { if (stats != NULL) { accumulate_and_reset_statistics(stats); } //如果当前 TLAB 有效 if (end() != NULL) { invariants(); //将用了的空间记录如线程分配对象大小记录 thread()->incr_allocated_bytes(used_bytes()); //填充dummy object insert_filler(); //清空当前 TLAB 指针 initialize(NULL, NULL, NULL); } }
9.4. GC 相关 TLAB 操作
9.4.1. GC 前
不同的 GC 可能实现不一样,但是 TLAB 操作的时机是基本一样的,这里以 G1 GC 为例,在真正 GC 前:
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
void G1CollectedHeap::gc_prologue(bool full) { //省略其他代码 // Fill TLAB's and such { Ticks start = Ticks::now(); //确保堆内存是可以解析的 ensure_parsability(true); Tickspan dt = Ticks::now() - start; phase_times()->record_prepare_tlab_time_ms(dt.seconds() * MILLIUNITS); } //省略其他代码 }
为何要确保堆内存是可以解析的呢?这样有利于更快速的扫描堆上对象。确保内存可以解析里面做了什么呢?其实主要就是退还每个线程的 TLAB 以及填充 dummy object。
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
void CollectedHeap::ensure_parsability(bool retire_tlabs) { //真正的 GC 肯定发生在安全点上,这个在后面安全点章节会详细说明 assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), "Should only be called at a safepoint or at start-up"); ThreadLocalAllocStats stats; for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next();) { BarrierSet::barrier_set()->make_parsable(thread); //如果全局启用了 TLAB if (UseTLAB) { //如果指定要回收,则回收 TLAB if (retire_tlabs) { //回收 TLAB,调用 9.3.2.3. 当前 TLAB 放回堆 提到的 retire 方法 thread->tlab().retire(&stats); } else { //当前如果不回收,则将 TLAB 填充 Dummy Object 利于解析 thread->tlab().make_parsable(); } } } stats.publish(); }
9.4.2. GC 后
不同的 GC 可能实现不一样,但是 TLAB 操作的时机是基本一样的,这里以 G1 GC 为例,在 GC 后:
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
_desired_size
是什么时候变得呢?怎么变得呢?
void G1CollectedHeap::gc_epilogue(bool full) { //省略其他代码 resize_all_tlabs(); }
src/hotspot/share/gc/shared/collectedHeap.cpp
void CollectedHeap::resize_all_tlabs() { //需要在安全点,GC 会处于安全点的 assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), "Should only resize tlabs at safepoint"); //如果 UseTLAB 和 ResizeTLAB 都是打开的(默认就是打开的) if (UseTLAB && ResizeTLAB) { for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { //重新计算每个线程 TLAB 期望大小 thread->tlab().resize(); } } }
重新计算每个线程 TLAB 期望大小: src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
void ThreadLocalAllocBuffer::resize() { assert(ResizeTLAB, "Should not call this otherwise"); //根据 _allocation_fraction 这个 EMA 采集得出平均数乘以Eden区大小,得出 TLAB 当前预测占用内存比例 size_t alloc = (size_t)(_allocation_fraction.average() * (Universe::heap()->tlab_capacity(thread()) / HeapWordSize)); //除以目标 refill 次数就是新的 TLAB 大小,和初始化时候的计算方法差不多 size_t new_size = alloc / _target_refills; //保证在 min_size 还有 max_size 之间 new_size = clamp(new_size, min_size(), max_size()); size_t aligned_new_size = align_object_size(new_size); log_trace(gc, tlab)("TLAB new size: thread: " INTPTR_FORMAT " [id: %2d]" " refills %d alloc: %8.6f desired_size: " SIZE_FORMAT " -> " SIZE_FORMAT, p2i(thread()), thread()->osthread()->thread_id(), _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); //设置新的 TLAB 大小 set_desired_size(aligned_new_size); //重置 TLAB 最大浪费空间 set_refill_waste_limit(initial_refill_waste_limit()); }