在initialize_heap_sizes中初始化了gc的线程,后续的gc以任务的形式提交
主要有三种:
1.触发gc操作一般在分配内存时,空间不足可能要oom时才寻找合适的stw时机
(某面试官说道:有人的地方就有江湖,有分配内存的地方都可能oom)
2.system.gc()
3.定时任务G1PeriodicGCTask
想看gc方法找gcRoots什么的需要一个切入点,定时任务作为一个保底的触发机制,可以不断尝试触发gc
先看简单的定时任务
void G1PeriodicGCTask::execute() {
check_for_periodic_gc();
// G1PeriodicGCInterval is a manageable flag and can be updated
// during runtime. If no value is set, wait a second and run it
// again to see if the value has been updated. Otherwise use the
// real value provided.
//这个意思就是如果后续运行过程中进行了参数调整,可以实时调整
schedule(G1PeriodicGCInterval == 0 ? 1000 : G1PeriodicGCInterval);
}
void ConcurrentGCThread::run() {
// Setup handle area
set_active_handles(JNIHandleBlock::allocate_block());
// Wait for initialization to complete
wait_init_completed();
run_service();
// Signal thread has terminated
MonitorLocker ml(Terminator_lock);
Atomic::release_store(&_has_terminated, true);
ml.notify_all();
}
定时任务提交给了G1ServiceThread,这个类继承到最上面是一个javaThread,和java一样看run方法就行,最后调用到了task中的execute,
在执行check_for_periodic_gc()完成后设置下一次的定时任务
void G1PeriodicGCTask::check_for_periodic_gc() {
// If disabled, just return.
// 间隔设置成0后将不会执行gc,直接开启下一秒的gc任务
if (G1PeriodicGCInterval == 0) {
return;
}
//try_collect就是检查过程,真的要gc的话看里面
log_debug(gc, periodic)("Checking for periodic GC.");
if (should_start_periodic_gc()) {
if (!G1CollectedHeap::heap()->try_collect(GCCause::_g1_periodic_collection)) {
log_debug(gc, periodic)("GC request denied. Skipping.");
}
}
}
这里的GCCause::_g1_periodic_collection为25
bool G1CollectedHeap::try_collect(GCCause::Cause cause) {
//堆锁判断是否是当前线程的锁
assert_heap_not_locked();
// Lock to get consistent set of values.
uint gc_count_before;
uint full_gc_count_before;
uint old_marking_started_before;
{
MutexLocker ml(Heap_lock);
gc_count_before = total_collections();
full_gc_count_before = total_full_collections();
old_marking_started_before = _old_marking_cycles_started;
}
//判断是否触发并发的full gc
if (should_do_concurrent_full_gc(cause)) {
return try_collect_concurrently(cause,
gc_count_before,
old_marking_started_before);
} else if (GCLocker::should_discard(cause, gc_count_before)) {
//判断是否是初始化gc并且获取的gc次数被修改过
// Indicate failure to be consistent with VMOp failure due to
// another collection slipping in after our gc_count but before
// our request is processed.
return false;
} else if (cause == GCCause::_gc_locker || cause == GCCause::_wb_young_gc
DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) {
//白盒子测试或者初始化
// Schedule a standard evacuation pause. We're setting word_size
// to 0 which means that we are not requesting a post-GC allocation.
VM_G1CollectForAllocation op(0, /* word_size */
gc_count_before,
cause,
policy()->max_pause_time_ms());
VMThread::execute(&op);
return op.gc_succeeded();
} else {
// Schedule a Full GC.
//简单的full gc
VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
VMThread::execute(&op);
return op.gc_succeeded();
}
}
gc有多少种cause可以查看GCCause.cpp
case _java_lang_system_gc:
return "System.gc()";
case _full_gc_alot:
return "FullGCAlot";
case _scavenge_alot:
return "ScavengeAlot";
case _allocation_profiler:
return "Allocation Profiler";
case _jvmti_force_gc:
return "JvmtiEnv ForceGarbageCollection";
case _gc_locker:
return "GCLocker Initiated GC";
case _heap_inspection:
return "Heap Inspection Initiated GC";
case _heap_dump:
return "Heap Dump Initiated GC";
//...省略
case _g1_inc_collection_pause:
return "G1 Evacuation Pause";
通过这个参数可以统计出整个jvm已经进行的gc类型,比如jstat -gccause <pid> 100 (意思是每秒打印gc信息)
其中有一个字段LGCC对应这个原因
C:\Users\tango>jstat -gccause 21136 1000
S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT LGCC GCC
0.00 97.40 10.99 64.32 99.07 95.76 54 0.921 0 0.000 6 0.013 0.934 G1 Evacuation Pause No GC
0.00 97.40 10.99 64.32 99.07 95.76 54 0.921 0 0.000 6 0.013 0.934 G1 Evacuation Pause No GC
0.00 97.40 10.99 64.32 99.07 95.76 54 0.921 0 0.000 6 0.013 0.934 G1 Evacuation Pause No GC
0.00 97.40 10.99 64.32 99.07 95.76 54 0.921 0 0.000 6 0.013 0.934 G1 Evacuation Pause No GC
jvm内存触发
结合前面内存分配的过程就能快速找到
tlab触发##
开启tlab后,如果内存不够走tlab_slow分配会调用这里,注意这里需要分配的东西是tlab的空间,与后续heap直接分配的方法一样但是对象不一样
HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size,
size_t requested_size,
size_t* actual_size) {
//safepoint就是安全点,jvm线程执行字节码自己能够知道
assert_heap_not_locked_and_not_at_safepoint();
//大对象直接走的heap,tlab不走
assert(!is_humongous(requested_size), "we do not allow humongous TLABs");
return attempt_allocation(min_size, requested_size, actual_size);
}
attempt_allocation和heap触发的一样
heap触发##
HeapWord* MemAllocator::allocate_outside_tlab(Allocation& allocation) const {
allocation._allocated_outside_tlab = true;
HeapWord* mem = Universe::heap()->mem_allocate(_word_size, &allocation._overhead_limit_exceeded);
if (mem == NULL) {
return mem;
}
NOT_PRODUCT(Universe::heap()->check_for_non_bad_heap_word_value(mem, _word_size));
size_t size_in_bytes = _word_size * HeapWordSize;
_thread->incr_allocated_bytes(size_in_bytes);
return mem;
}
mem_allocate
HeapWord*
G1CollectedHeap::mem_allocate(size_t word_size,
bool* gc_overhead_limit_was_exceeded) {
assert_heap_not_locked_and_not_at_safepoint();
if (is_humongous(word_size)) {
return attempt_allocation_humongous(word_size);
}
size_t dummy = 0;
//和tlab_slow一个方法
return attempt_allocation(word_size, word_size, &dummy);
}
attempt_allocation_humongous
里面可以触发gc原因的有
_g1_humongous_allocation
_g1_preventive_collection
特别注意大对象触发时,由于大对象消耗内存很快,所以会触发检查是否要进行并发标记
// Humongous objects can exhaust the heap quickly, so we should check if we
// need to start a marking cycle at each humongous object allocation. We do
// the check before we do the actual allocation. The reason for doing it
// before the allocation is that we avoid having to keep track of the newly
// allocated memory while we do a GC.
if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
word_size)) {
collect(GCCause::_g1_humongous_allocation);
}
attempt_allocation
inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
assert_heap_not_locked_and_not_at_safepoint();
assert(!is_humongous(desired_word_size), "attempt_allocation() should not "
"be called for humongous allocation requests");
//numa从系统的多线程空间获取内存
HeapWord* result = _allocator->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
//numa分配失败
if (result == NULL) {
*actual_word_size = desired_word_size;
//内部可能触发gc再进行内存分配
result = attempt_allocation_slow(desired_word_size);
}
assert_heap_not_locked();
if (result != NULL) {
assert(*actual_word_size != 0, "Actual size must have been set here");
dirty_young_block(result, *actual_word_size);
} else {
*actual_word_size = 0;
}
return result;
}
system.gc()
JVM_ENTRY_NO_ENV(void, JVM_GC(void))
if (!DisableExplicitGC) {
EventSystemGC event;
event.set_invokedConcurrent(ExplicitGCInvokesConcurrent);
Universe::heap()->collect(GCCause::_java_lang_system_gc);
event.commit();
}
JVM_END
//collect
void G1CollectedHeap::collect(GCCause::Cause cause) {
try_collect(cause);
}
回到了开始的定时任务,而且人家还是一秒一次的
如果版本支持G1PeriodicGCTask这个system.gc()就不要写了吧