Skip to content

g1 evacuation_Young_GC_Condition

ceragon edited this page Jan 1, 2022 · 6 revisions

Young GC 的触发条件

下面的代码都有省略,省略的原因是为了突出显示和 Young GC 触发条件有关的内容

概括

触发 Young GC 的方法

相关代码
HeapWord *G1CollectedHeap::attempt_allocation_slow(size_t word_size) {
    HeapWord *result = NULL;
    for (uint try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) {
        bool should_try_gc;
        uint gc_count_before;
        {
            MutexLockerEx x(Heap_lock);
            // 如果获取到 Heap_lock 会进入到这里
            // 下面代码会尝试在 heap 的 eden 区申请一块内存,大小是 word_size。
            // 如果现有的 eden 区不满足分配,会尝试申请激活一个新的 eden region。
            // 能否申请成功,取决于一些条件
            result = _allocator->attempt_allocation_locked(word_size);
            if (result != NULL) {
                return result;
            }
            // 说明当前的 eden 区满了,所以走到了这里
            // TODO: GCLocker 这个还没弄明白,但可以确认一点,为真的概率不大
            // 我猜一下下面的逻辑。如果上一次 Young GC 被 GCLocker 打断了
            // 而且还满足了临时扩充 eden 区的条件,会走下面的强制分配方法
            if (GCLocker::is_active_and_needs_gc() && g1_policy()->can_expand_young_list()) {
                result = _allocator->attempt_allocation_force(word_size);
                if (result != NULL) {
                    return result;
                }
            }
            should_try_gc = !GCLocker::needs_gc();
            gc_count_before = total_collections();
        }
        // 如果上一次的 Young GC 没有被 GCLocker 打断,则这里为真
        if (should_try_gc) {
            bool succeeded;
            // ————————————————————————————————————— 同步执行一次 GC ——————————————————————————————————
            result = do_collection_pause(word_size, gc_count_before, &succeeded, GCCause::_g1_inc_collection_pause);
            // ————————————————————————————————————————————————————————————————————————————————————————————————————
            if (result != NULL) {
                return result;
            }
            // GC 执行成功了,但依然没有足够空间分配 word_size 的大小
            if (succeeded) {
                return NULL;
            }
        } else {
            // GCLockerRetryAllocationCount 的默认值是 2
            if (gclocker_retry_count > GCLockerRetryAllocationCount) {
                return NULL;
            }
            GCLocker::stall_until_clear();
            gclocker_retry_count += 1;
        }
        size_t dummy = 0;
        // 尝试普通分配,以下是官方解释
        // 如果我们没有成功调度一次 GC(因为另一个线程打败了我们)或者我们由于 GC 锁定器而停止,我们可以到达这里。 
        // 在任何一种情况下,我们都应该重试分配尝试,以防另一个线程成功执行收集并回收足够的空间。 
        // 我们在这里进行第一次尝试(不持有 Heap_lock),后续尝试将在下一次循环迭代开始时(在获取 Heap_lock 之后)。 
        result = _allocator->attempt_allocation(word_size, word_size, &dummy);
        if (result != NULL) {
            return result;
        }
    }
    return NULL;
}

调用路径

路径
  1. 下面方法调用了触发 Young GC 的方法。如果再反查下面方法的调用方法,会看到了两个方法。后面介绍了
inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size, size_t desired_word_size, size_t* actual_word_size) {
    HeapWord* result = _allocator->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
    if (result == NULL) {
        // 分配失败
        *actual_word_size = desired_word_size;
        // 尝试慢分配 (因为分配过程涉及到锁,甚至有引发 GC 的可能)
        result = attempt_allocation_slow(desired_word_size);
        // ————————————————————————————————————————————————
    }
}

调用方法一

代码
  1. 相关代码如下
HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) {
    // 在这里调用了 G1CollectedHeap::attempt_allocation() 方法
    return attempt_allocation(min_size, requested_size, actual_size);
    // ——————————————————————————————————————————————————————————————
}
HeapWord *MemAllocator::allocate_inside_tlab_slow(Allocation &allocation) const {
    HeapWord *mem = NULL;
    ThreadLocalAllocBuffer &tlab = _thread->tlab();
    // free() 表示 TLAB 的剩余空间,单位是字。
    // refill_waste_limit() 表示 TLAB 空间浪费阈值。
    // 阈值 = TLAB 大小 / TLABRefillWasteFraction。TLABRefillWasteFraction 的默认值是64
    // 这里的意思可以理解为,如果 TLAB 分配不了了,但是可用空间在总 TLAB 空间的 1/64 之上,就为真
    if (tlab.free() > tlab.refill_waste_limit()) {
        return NULL;
    }
    // compute_size 是用来计算新的 TLAB 的大小。
    // _word_size 是当前待创建对象的大小
    size_t new_tlab_size = tlab.compute_size(_word_size);
    // 为0只有一种情况,处于分配中的 eden region 无法容纳待创建对象
    if (new_tlab_size == 0) {
        return NULL;
    }
    // 在这里调用了 G1CollectedHeap::allocate_new_tlab() 方法
    mem = _heap->allocate_new_tlab(min_tlab_size, new_tlab_size, &allocation._allocated_tlab_size);
    // ——————————————————————————————————————————————————————————————————————————————————
    if (mem == NULL) {
        return NULL;
    }
    // 分配成功
    return mem;
}
HeapWord* MemAllocator::allocate_inside_tlab(Allocation& allocation) const {
    // 先尝试在 TLAB 中分配
    HeapWord* mem = _thread->tlab().allocate(_word_size);
    if (mem != NULL) {
        return mem;
    }
    // TLAB 空间不足,于是调用上面的那个方法
    return allocate_inside_tlab_slow(allocation);
    // ————————————————————————————————————————
}

调用分支二

分支代码
HeapWord* G1CollectedHeap::mem_allocate(size_t word_size, bool*  gc_overhead_limit_was_exceeded) {
    if (is_humongous(word_size)) {
        // 大对象走专属的分配流程
        return attempt_allocation_humongous(word_size);
    }
    size_t dummy = 0;
    // 调用上面的 G1CollectedHeap::attempt_allocation() 方法
    return attempt_allocation(word_size, word_size, &dummy);
    // ————————————————————————————————————————————————
}
HeapWord *MemAllocator::allocate_outside_tlab(Allocation &allocation) const {
    // 调用上面的 mem_allocate 方法
    HeapWord *mem = _heap->mem_allocate(_word_size, &allocation._overhead_limit_exceeded);
    // ——————————————————————————————————————————————————————————————————————————————————
    if (mem == NULL) {
        return mem;
    }
    return mem;
}

合并调用

解析
HeapWord* MemAllocator::mem_allocate(Allocation& allocation) const {
    if (UseTLAB) {
        // 默认 TLAB 是开启的
        // 尝试在 TLAB 中分配对象,
        // 如果 TLAB 空间不足就尝试在当前分配中的 eden region 分配
        HeapWord* result = allocate_inside_tlab(allocation);
        if (result != NULL) {
            return result;
        }
    }
    // 走到这里,说明 TLAB 和分配中的 eden region 没有足够空间
    // 所谓的 outside 可以理解为非当前分配中的 eden region
    // 所以这里是在向
    return allocate_outside_tlab(allocation);
}
oop MemAllocator::allocate() const {
    oop obj = NULL;
    {
        // obj 是一个待分配空间的对象
        Allocation allocation(*this, &obj);
        // 执行 obj 对象的空间分配工作
        HeapWord *mem = mem_allocate(allocation);
    }
    return obj;
}

attempt_allocation_slow() 方法相关信息

attempt_allocation_locked() 方法解析

解析
inline HeapWord *G1Allocator::attempt_allocation_locked(size_t word_size) {
    HeapWord *result = mutator_alloc_region()->attempt_allocation_locked(word_size);
    return result;
}
inline HeapWord* G1AllocRegion::attempt_allocation_locked(size_t word_size) {
    return attempt_allocation_locked(word_size, word_size, &temp);
}
inline HeapWord *G1AllocRegion::attempt_allocation_locked(size_t min_word_size,
                                                          size_t desired_word_size,
                                                          size_t *actual_word_size) {
    // 通过加锁的方式在 _alloc_region 中申请一块新的 TLAB 空间
    HeapWord *result = attempt_allocation(min_word_size, desired_word_size, actual_word_size);
    if (result != NULL) {
        return result;
    }
    // 尝试申请一个新的 eden region,并尝试在这块空间中分配。
    // 第二个参数 false 表示申请 eden region 受最大值约束
    result = new_alloc_region_and_allocate(desired_word_size, false /* force */);
    if (result != NULL) {
        return result;
    }
    return NULL;
}

attempt_allocation_force() 方法解析

解析
inline HeapWord* G1Allocator::attempt_allocation_force(size_t word_size) {
    return mutator_alloc_region()->attempt_allocation_force(word_size);
}
inline HeapWord *G1AllocRegion::attempt_allocation_force(size_t word_size) {
    // 本方法是在 G1AllocRegion 的子类 MutatorAllocRegion 中运行
    // 第二个参数 true 表示无视 eden 区的上限,只要 heap 空间足够就分配新的 region
    HeapWord *result = new_alloc_region_and_allocate(word_size, true /* force */);
    if (result != NULL) {
        return result;
    }
    return NULL;
}

do_collection_pause() 方法解析

解析
HeapWord *G1CollectedHeap::do_collection_pause(size_t word_size,
                                               uint gc_count_before,
                                               bool *succeeded,
                                               GCCause::Cause gc_cause) {
    VM_G1CollectForAllocation op(word_size,
                                 gc_count_before,
                                 gc_cause,
                                 false, /* should_initiate_conc_mark */
                                 g1_policy()->max_pause_time_ms());
    // 在 VMThread 中执行 GC 的逻辑,并且是同步执行
    // 执行 GC 的时候会先进行 STW
    VMThread::execute(&op);
    HeapWord *result = op.result();
    bool ret_succeeded = op.prologue_succeeded() && op.pause_succeeded();
    *succeeded = ret_succeeded;
    return result;
}

attempt_allocation() 方法解析

解析
inline HeapWord* G1Allocator::attempt_allocation(size_t min_word_size,
                                                 size_t desired_word_size,
                                                 size_t* actual_word_size) {
    HeapWord* result = mutator_alloc_region()->attempt_retained_allocation(min_word_size, desired_word_size, actual_word_size);
    if (result != NULL) {
        return result;
    }
    return mutator_alloc_region()->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
}

new_alloc_region_and_allocate() 解读

解读
HeapWord *G1AllocRegion::new_alloc_region_and_allocate(size_t word_size, bool force) {
    HeapRegion *new_alloc_region = allocate_new_region(word_size, force);
    if (new_alloc_region != NULL) {
        new_alloc_region->reset_pre_dummy_top();
        _used_bytes_before = new_alloc_region->used();
        HeapWord *result = allocate(new_alloc_region, word_size);
        OrderAccess::storestore();
        update_alloc_region(new_alloc_region);
        return result;
    } else {
        return NULL;
    }
}

Clone this wiki locally