java ygc逐步增加,【jvm学习笔记五】G1-YGC分析

在内存分配的时候,如果剩余空间不能满足分配的对象就会触发YGC。G1每次都会收集所有的新生代的分区,但新生代分区的数目每次可能是不一样的,这是因为G1会根据时间预测模型来调整新生代分区数目。

YGC算法步骤

YGC算法主要分并行和其他部分,主要步骤如下:

收集之前STW;

选择要收集的Cset,即整个新生代分区

进入并行处理:

3.1 根扫描并处理:根直接引用的对象放到s区,然后把对象的field入栈等待后续复制处理;

3.2 处理老年代到新生代的引用:更新RSet,从RSet出发,把RSet所在卡表对应的分区内存块所有对象认为是根,并将根引用的对象复制到s区,其field入栈等待后续复制处理;

3.3 JIT代码扫描

3.4 递归处理栈中的对象

其他处理:

4.1 JIT代码位置更新

4.2 引用处理

4.3 字符串去重优化回收

4.4 清除卡表

4.5 JIT代码回收

4.6 Redirty、释放CSet、尝试大对象回收等

4.7 可能启动并发标记:内存超过阈值,则启动

并行任务处理

cf708ab71fec

image.png

并行任务是图中的第二步

GC worker start (g1CollectedHeap)

void work(uint worker_id) {

if (worker_id >= _n_workers) return; // no work needed this round

double start_time_ms = os::elapsedTime() * 1000.0;

_g1h->g1_policy()->phase_times()->record_gc_worker_start_time(worker_id, start_time_ms);

...

}

Ext Root Scanning(g1CollectedHeap)

void

G1CollectedHeap::

g1_process_roots(OopClosure* scan_non_heap_roots,

OopClosure* scan_non_heap_weak_roots,

OopsInHeapRegionClosure* scan_rs,

CLDClosure* scan_strong_clds,

CLDClosure* scan_weak_clds,

CodeBlobClosure* scan_strong_code,

uint worker_i) {

// First scan the shared roots.

double ext_roots_start = os::elapsedTime();

double closure_app_time_sec = 0.0;

bool during_im = _g1h->g1_policy()->during_initial_mark_pause();

bool trace_metadata = during_im && ClassUnloadingWithConcurrentMark;

BufferingOopClosure buf_scan_non_heap_roots(scan_non_heap_roots);

BufferingOopClosure buf_scan_non_heap_weak_roots(scan_non_heap_weak_roots);

process_roots(false, // no scoping; this is parallel code

SharedHeap::SO_None,

&buf_scan_non_heap_roots,

&buf_scan_non_heap_weak_roots,

scan_strong_clds,

// Unloading Initial Marks handle the weak CLDs separately.

(trace_metadata ? NULL : scan_weak_clds),

scan_strong_code);

// Now the CM ref_processor roots.

if (!_process_strong_tasks->is_task_claimed(G1H_PS_refProcessor_oops_do)) {

// We need to treat the discovered reference lists of the

// concurrent mark ref processor as roots and keep entries

// (which are added by the marking threads) on them live

// until they can be processed at the end of marking.

ref_processor_cm()->weak_oops_do(&buf_scan_non_heap_roots);

}

if (trace_metadata) {

// Barrier to make sure all workers passed

// the strong CLD and strong nmethods phases.

active_strong_roots_scope()->wait_until_all_workers_done_with_threads(n_par_threads());

// Now take the complement of the strong CLDs.

ClassLoaderDataGraph::roots_cld_do(NULL, scan_weak_clds);

}

// Finish up any enqueued closure apps (attributed as object copy time).

buf_scan_non_heap_roots.done();

buf_scan_non_heap_weak_roots.done();

double obj_copy_time_sec = buf_scan_non_heap_roots.closure_app_seconds()

+ buf_scan_non_heap_weak_roots.closure_app_seconds();

g1_policy()->phase_times()->record_obj_copy_time(worker_i, obj_copy_time_sec * 1000.0);

double ext_root_time_ms =

((os::elapsedTime() - ext_roots_start) - obj_copy_time_sec) * 1000.0;

g1_policy()->phase_times()->record_ext_root_scan_time(worker_i, ext_root_time_ms);

// During conc marking we have to filter the per-thread SATB buffers

// to make sure we remove any oops into the CSet (which will show up

// as implicitly live).

double satb_filtering_ms = 0.0;

if (!_process_strong_tasks->is_task_claimed(G1H_PS_filter_satb_buffers)) {

if (mark_in_progress()) {

double satb_filter_start = os::elapsedTime();

JavaThread::satb_mark_queue_set().filter_thread_buffers();

satb_filtering_ms = (os::elapsedTime() - satb_filter_start) * 1000.0;

}

}

g1_policy()->phase_times()->record_satb_filtering_time(worker_i, satb_filtering_ms);

// Now scan the complement of the collection set.

G1CodeBlobClosure scavenge_cs_nmethods(scan_non_heap_weak_roots);

g1_rem_set()->oops_into_collection_set_do(scan_rs, &scavenge_cs_nmethods, worker_i);

_process_strong_tasks->all_tasks_completed();

}

在process_root方法中,会通过Threads::possibly_parallel_oops_do方法遍历所有的java线程和VMThread线程进行栈处理

void JavaThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) {

// 处理JNI本地栈、JVM内部本地方法栈

Thread::oops_do(f, cld_f, cf);

if (has_last_Java_frame()) {

// Record JavaThread to GC thread

RememberProcessedThread rpt(this);

// 处理用于实现安全功能的类

if (_privileged_stack_top != NULL) {

_privileged_stack_top->oops_do(f);

}

// traverse the registered growable array

if (_array_for_gc != NULL) {

for (int index = 0; index < _array_for_gc->length(); index++) {

f->do_oop(_array_for_gc->adr_at(index));

}

}

// 处理monitor块

for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) {

chunk->oops_do(f);

}

// 遍历栈

for(StackFrameStream fst(this); !fst.is_done(); fst.next()) {

fst.current()->oops_do(f, cld_f, cf, fst.register_map());

}

}

// callee_target is never live across a gc point so NULL it here should

// it still contain a methdOop.

set_callee_target(NULL);

assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!");

// If we have deferred set_locals there might be oops waiting to be

// written

GrowableArray* list = deferred_locals();

if (list != NULL) {

for (int i = 0; i < list->length(); i++) {

list->at(i)->oops_do(f);

}

}

// 遍历这些实例对象,它们可能引用了堆对象

f->do_oop((oop*) &_threadObj);

f->do_oop((oop*) &_vm_result);

f->do_oop((oop*) &_exception_oop);

f->do_oop((oop*) &_pending_async_exception);

if (jvmti_thread_state() != NULL) {

jvmti_thread_state()->oops_do(f);

}

}

update RSet (g1RemSet)

void G1RemSet::updateRS(DirtyCardQueue* into_cset_dcq, uint worker_i) {

double start = os::elapsedTime();

// Apply the given closure to all remaining log entries.

//使用closure处理DCQ队列

RefineRecordRefsIntoCSCardTableEntryClosure into_cset_update_rs_cl(_g1, into_cset_dcq);

// 遍历处理dirty_card

_g1->iterate_dirty_card_closure(&into_cset_update_rs_cl, into_cset_dcq, false, worker_i);

// Now there should be no dirty cards.

if (G1RSLogCheckCardTable) {

CountNonCleanMemRegionClosure cl(_g1);

_ct_bs->mod_card_iterate(&cl);

// XXX This isn't true any more: keeping cards of young regions

// marked dirty broke it. Need some reasonable fix.

guarantee(cl.n() == 0, "Card table should be clean.");

}

_g1p->phase_times()->record_update_rs_time(worker_i, (os::elapsedTime() - start) * 1000.0);

}

void G1CollectedHeap::iterate_dirty_card_closure(CardTableEntryClosure* cl,

DirtyCardQueue* into_cset_dcq,

bool concurrent,

uint worker_i) {

// Clean cards in the hot card cache

//处理热表

G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();

hot_card_cache->drain(worker_i, g1_rem_set(), into_cset_dcq);

//处理DCQS中剩下的DCQ

DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();

int n_completed_buffers = 0;

while (dcqs.apply_closure_to_completed_buffer(cl, worker_i, 0, true)) {

n_completed_buffers++;

}

g1_policy()->phase_times()->record_update_rs_processed_buffers(worker_i, n_completed_buffers);

dcqs.clear_n_completed_buffers();

assert(!dcqs.completed_buffers_exist_dirty(), "Completed buffers exist!");

}

Scan Rs (G1RemSet)

void G1RemSet::scanRS(OopsInHeapRegionClosure* oc,

CodeBlobClosure* code_root_cl,

uint worker_i) {

double rs_time_start = os::elapsedTime();

//每个线程处理部分分区

HeapRegion *startRegion = _g1->start_cset_region_for_worker(worker_i);

ScanRSClosure scanRScl(oc, code_root_cl, worker_i);

//第一次扫描,处理一般对象

_g1->collection_set_iterate_from(startRegion, &scanRScl);

scanRScl.set_try_claimed();

//第二次扫描,处理代码对象

_g1->collection_set_iterate_from(startRegion, &scanRScl);

double scan_rs_time_sec = (os::elapsedTime() - rs_time_start)

- scanRScl.strong_code_root_scan_time_sec();

assert(_cards_scanned != NULL, "invariant");

_cards_scanned[worker_i] = scanRScl.cards_done();

_g1p->phase_times()->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0);

_g1p->phase_times()->record_strong_code_root_scan_time(worker_i,

scanRScl.strong_code_root_scan_time_sec() * 1000.0);

}

Code Root Scaning

void scan_strong_code_roots(HeapRegion* r) {

double scan_start = os::elapsedTime();

r->strong_code_roots_do(_code_root_cl);

_strong_code_root_scan_time_sec += (os::elapsedTime() - scan_start);

}

Object Copy (g1CollectedHeap)

在遍历java栈时,会将对象复制到s区

void G1ParCopyClosure::do_oop_work(T* p) {

T heap_oop = oopDesc::load_heap_oop(p);

if (oopDesc::is_null(heap_oop)) {

return;

}

oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);

G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj);

if (state == G1CollectedHeap::InCSet) {

oop forwardee;

//对象是否已复制完成

if (obj->is_forwarded()) {

forwardee = obj->forwardee();

} else {

//复制对象到s区

forwardee = _par_scan_state->copy_to_survivor_space(obj);

}

assert(forwardee != NULL, "forwardee should not be NULL");

oopDesc::encode_store_heap_oop(p, forwardee);

if (do_mark_object != G1MarkNone && forwardee != obj) {

// 如果对象成功复制,把对象的新地址设置到老对象的对象头.

mark_forwarded_object(obj, forwardee);

}

if (barrier == G1BarrierKlass) {

do_klass_barrier(p, forwardee);

}

} else {

//对不在CSet中的对象,先标记为活的,到时作为跟对象

if (state == G1CollectedHeap::IsHumongous) {

_g1->set_humongous_is_live(obj);

}

// The object is not in collection set. If we're a root scanning

// closure during an initial mark pause then attempt to mark the object.

if (do_mark_object == G1MarkFromRoot) {

mark_object(obj);

}

}

//如果是eval失败,则将对象记录在一个队列中

if (barrier == G1BarrierEvac) {

_par_scan_state->update_rs(_from, p, _worker_id);

}

}

copy对象到s区的实现

oop G1ParScanThreadState::copy_to_survivor_space(oop const old) {

size_t word_sz = old->size();

HeapRegion* from_region = _g1h->heap_region_containing_raw(old);

// +1 to make the -1 indexes valid...

int young_index = from_region->young_index_in_cset()+1;

G1CollectorPolicy* g1p = _g1h->g1_policy();

markOop m = old->mark();

//根据age和s区是否放的下来判断对象是copy到s区还是old区

int age = m->has_displaced_mark_helper() ? m->displaced_mark_helper()->age()

: m->age();

GCAllocPurpose alloc_purpose = g1p->evacuation_destination(from_region, age,

word_sz);

AllocationContext_t context = from_region->allocation_context();

//使用plab方法在plab分配(plab和tlab一样是为了避免并发,只是plab是分配在s和old区)

HeapWord* obj_ptr = _g1_par_allocator->allocate(alloc_purpose, word_sz, context);

if (_g1h->evacuation_should_fail()) {

if (obj_ptr != NULL) {

_g1_par_allocator->undo_allocation(alloc_purpose, obj_ptr, word_sz, context);

obj_ptr = NULL;

}

}

if (obj_ptr == NULL) {

// plab分配失败,则判断是否需要再分配plab,大小由youngPLABSzie和OldPLABSize决定,当然还有浪费的比例ParallelGCBufferWasterPct参数

return _g1h->handle_evacuation_failure_par(this, old);

}

oop obj = oop(obj_ptr);

// We're going to allocate linearly, so might as well prefetch ahead.

Prefetch::write(obj_ptr, PrefetchCopyIntervalInBytes);

oop forward_ptr = old->forward_to_atomic(obj);

if (forward_ptr == NULL) {

//如果对象头没有指针,说明是第一次复制,增加引用关系

Copy::aligned_disjoint_words((HeapWord*) old, obj_ptr, word_sz);

HeapRegion* to_region = _g1h->heap_region_containing_raw(obj_ptr);

alloc_purpose = to_region->is_young() ? GCAllocForSurvived : GCAllocForTenured;

if (g1p->track_object_age(alloc_purpose)) {

if (m->has_displaced_mark_helper()) {

//更新age信息和对象头

obj->set_mark(m);

obj->incr_age();

} else {

m = m->incr_age();

obj->set_mark(m);

}

age_table()->add(obj, word_sz);

} else {

obj->set_mark(m);

}

//字符串去重

if (G1StringDedup::is_enabled()) {

G1StringDedup::enqueue_from_evacuation(from_region->is_young(),

to_region->is_young(),

queue_num(),

obj);

}

size_t* surv_young_words = surviving_young_words();

surv_young_words[young_index] += word_sz;

//处理数组对象,放入队列后续处理,防止数组过大深度遍历时导致处理队列溢出

if (obj->is_objArray() && arrayOop(obj)->length() >= ParGCArrayScanChunk) {

arrayOop(obj)->set_length(0);

oop* old_p = set_partial_array_mask(old);

push_on_queue(old_p);

} else {

//

obj->oop_iterate_backwards(&_scanner);

}

} else {

_g1_par_allocator->undo_allocation(alloc_purpose, obj_ptr, word_sz, context);

obj = forward_ptr;

}

return obj;

}

处理每个对象的field

inline void G1CMOopClosure::do_oop_nv(T* p) {

oop obj = oopDesc::load_decode_heap_oop(p);

if (_cm->verbose_high()) {

gclog_or_tty->print_cr("[%u] we're looking at location "

"*"PTR_FORMAT" = "PTR_FORMAT,

_task->worker_id(), p2i(p), p2i((void*) obj));

}

//遍历对象的每个field进行处理

_task->deal_with_reference(obj);

}

主要部分就到这里了,最后再。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值