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 可能启动并发标记:内存超过阈值,则启动
并行任务处理
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);
}
主要部分就到这里了,最后再。。
java ygc逐步增加,【jvm学习笔记五】G1-YGC分析相关推荐
- 浅析java中的死锁_Java学习笔记五十五(死锁问题)
多线程死锁问题. 我们知道,多线程可以改善系统的资源利用率,并且可以提高程序的运行效率.但是,多线程也带来了新的问题,即:死锁问题. 1.死锁的概念 死锁可以理解为多个线程为了争夺同一个资源,而出现互 ...
- Java核心技术【卷一】——学习笔记(五)--泛型(一)
若要对某个特定类的特定关键字进行比较,可使用泛型来进行比较 若要对Student类的score字段进行比较,需要对Student implements Compareable接口 public cla ...
- praat学习笔记——五度值分析(石峰T值法)
一. 调域上下限的确定 语音录制了阴平"山".阳平"昨".上声"宝".去声"去"四个声调,每个声调用不同的字发了十遍音, ...
- 【算法学习笔记五】平摊分析
平摊分析 平摊分析是分析一个操作序列以显示每个操作的平均成本很小的任何策略,即使序列中的单个操作可能很昂贵.不同于平均案例分析:1)不涉及概率;2)保证最坏情况下各操作的平均性能. 三种常见的平摊方法 ...
- Java学习笔记(五):一张图总结完JVM8基础概念
Java学习笔记(五):一张图总结完JVM8基础概念 引文 最近在学习JVM的相关内容,好不容易把基础概念全部都学了一遍,却发现知识网络是零零散散的.迫不得已,只好再来一次总的归纳总结.为了更好的理解 ...
- JAVA学习笔记五---函数
JAVA学习笔记五---函数 5.1 方法的学习 编写一个程序,求圆的周长和面积. package practice; /*** 编写一个程序,求圆的周长和面积.* @author iszhangyo ...
- JVM学习笔记(Ⅰ):Class类文件结构解析(带你读懂Java字节码,这一篇就够了)
JVM学习笔记(Ⅰ):Class类文件结构解析,带你读懂Java字节码 前言:本文属于博主个人的学习笔记,博主也是小白.如果有不对的地方希望各位帮忙指出.本文主要还是我的学习总结,因为网上的一些知识分 ...
- Java之多线程学习笔记五 —— 多线程模拟龟兔赛跑
Java之多线程学习笔记五 -- 多线程模拟龟兔赛跑 参考教程B站狂神https://www.bilibili.com/video/BV1V4411p7EF package pers.ylw.less ...
- Java开发面试高频考点学习笔记(每日更新)
Java开发面试高频考点学习笔记(每日更新) 1.深拷贝和浅拷贝 2.接口和抽象类的区别 3.java的内存是怎么分配的 4.java中的泛型是什么?类型擦除是什么? 5.Java中的反射是什么 6. ...
最新文章
- java 多线程,及获取线程执行结果
- AQS.accquire
- HDU5763 Another Meaning(KMP+dp)
- Source Xref 与 JavaDocs 学习理解
- window.open打开页面并传值,window. location.search遍历获取到的请求链接中的所有参数
- hdu1166 敌兵布阵 线段树
- zynq创建ramip核
- 从程序员到项目经理(17):你不是一个人在战斗--思维一换天地宽【转载】
- python 执行文件的扩展名_python脚本文件的扩展名是什么
- 学习计算机组装与维护的意义,计算机组装与维护课程学习体会
- 计算机组装所需硬件,电脑组装机配置清单
- Android实现QQ空间图片下拉变大效果(雷惊风)
- 【微信公众号】6、SpringBoot整合WxJava创建自定义菜单
- 卫星遥感—地块/边界提取相关论文
- MyEclipse 目录结构简化
- GitHub——修改DNS提高git clone速度
- oracle连接工具 DBz,[Oracle] - 性能优化工具(3) - ADDM
- word快捷键粘贴不能用
- 老板:你们和外包有什么区别?
- java结账_java结账系统