在内存分配的时候,如果剩余空间不能满足分配的对象就会触发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 可能启动并发标记:内存超过阈值,则启动
并行任务处理
并行任务是图中的第二步
- 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<jvmtiDeferredLocalVariableSet*>* 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<barrier, do_mark_object>::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);
}
主要部分就到这里了,最后再。。