Garbage-First Garbage Collection
Garbage-First简称g1算法,是java目前可选的一个gc算法。
目标: 在一定的软实时性条件下,保证整体的吞吐
算法构成:
- 堆等大小: 整个内存堆被划分为相同大小的块。
The Garbage-First collector achieves these goals via sev- eral techniques. The heap is partitioned into a set of equal- sized heap regions, much like the train cars of the Mature- Object Space collector of Hudson and Moss [22]. However, whereas the remembered sets of the Mature-Object Space collector are unidirectional, recording pointers from older regions to younger but not vice versa, Garbage-First remem- bered sets record pointers from all regions (with some excep- tions, described in sections 2.4 and 4.6). Recording all ref- erences allows an arbitrary set of heap regions to be chosen for collection. A concurrent thread processes log records cre- ated by special mutator write barriers to keep remembered sets up-to-date, allowing shorter collections.
源码分析
- Young gc
void G1YoungCollector::collect() {
// Do timing/tracing/statistics/pre- and post-logging/verification work not
// directly related to the collection. They should not be accounted for in
// collection work timing.
// The G1YoungGCTraceTime message depends on collector state, so must come after
// determining collector state.
G1YoungGCTraceTime tm(this, _gc_cause);
// JFR
G1YoungGCJFRTracerMark jtm(gc_timer_stw(), gc_tracer_stw(), _gc_cause);
// JStat/MXBeans
G1MonitoringScope ms(monitoring_support(),
false /* full_gc */,
collector_state()->in_mixed_phase() /* all_memory_pools_affected */);
// Create the heap printer before internal pause timing to have
// heap information printed as last part of detailed GC log.
G1HeapPrinterMark hpm(_g1h);
// Young GC internal pause timing
G1YoungGCNotifyPauseMark npm(this);
// Verification may use the workers, so they must be set up before.
// Individual parallel phases may override this.
set_young_collection_default_active_worker_threads();
// Wait for root region scan here to make sure that it is done before any
// use of the STW workers to maximize cpu use (i.e. all cores are available
// just to do that).
wait_for_root_region_scanning();
G1YoungGCVerifierMark vm(this);
{
// Actual collection work starts and is executed (only) in this scope.
// Young GC internal collection timing. The elapsed time recorded in the
// policy for the collection deliberately elides verification (and some
// other trivial setup above).
policy()->record_young_collection_start();
calculate_collection_set(jtm.evacuation_info(), _target_pause_time_ms);
G1RedirtyCardsQueueSet rdcqs(G1BarrierSet::dirty_card_queue_set().allocator());
G1PreservedMarksSet preserved_marks_set(workers()->active_workers());
G1ParScanThreadStateSet per_thread_states(_g1h,
&rdcqs,
&preserved_marks_set,
workers()->active_workers(),
collection_set()->young_region_length(),
collection_set()->optional_region_length(),
&_evac_failure_regions);
pre_evacuate_collection_set(jtm.evacuation_info(), &per_thread_states);
bool may_do_optional_evacuation = collection_set()->optional_region_length() != 0;
// Actually do the work...
evacuate_initial_collection_set(&per_thread_states, may_do_optional_evacuation);
if (may_do_optional_evacuation) {
evacuate_optional_collection_set(&per_thread_states);
}
post_evacuate_collection_set(jtm.evacuation_info(), &per_thread_states);
// Refine the type of a concurrent mark operation now that we did the
// evacuation, eventually aborting it.
_concurrent_operation_is_full_mark = policy()->concurrent_operation_is_full_mark("Revise IHOP");
// Need to report the collection pause now since record_collection_pause_end()
// modifies it to the next state.
jtm.report_pause_type(collector_state()->young_gc_pause_type(_concurrent_operation_is_full_mark));
policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_failed());
}
TASKQUEUE_STATS_ONLY(print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
}
真正复制的代码
evacuate_live_objects(
MAYBE_INLINE_EVACUATION
void G1ParScanThreadState::dispatch_task(ScannerTask task) {
verify_task(task);
if (task.is_narrow_oop_ptr()) {
do_oop_evac(task.to_narrow_oop_ptr());
} else if (task.is_oop_ptr()) { //oop 复制
do_oop_evac(task.to_oop_ptr());
} else {
do_partial_array(task.to_partial_array_task());
}
}
堆栈:
Thread 23 "GC Thread#4" hit Breakpoint 1, G1ParScanThreadState::do_copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...) at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:443
443 assert(region_attr.is_in_cset(),
(gdb) bt
#0 G1ParScanThreadState::do_copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...) at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:443
#1 0x00007ffff64ab3f6 in G1ParScanThreadState::copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:555
#2 0x00007ffff64de15e in G1ParCopyClosure<(G1Barrier)0, false>::do_oop_work<oopDesc*> (this=0x7fff7c001478, p=0x7ffff02e1cc8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp:240
#3 0x00007ffff64dcbc3 in G1ParCopyClosure<(G1Barrier)0, false>::do_oop (this=0x7fff7c001478, p=0x7ffff02e1cc8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1OopClosures.hpp:167
#4 0x00007ffff6546dd8 in chunk_oops_do (f=0x7fff7c001478, chunk=0x7ffff02e1cb0, chunk_top=0x7ffff02e1cd0 "\350\034\200\026\a") at /home/dai/jdk/src/hotspot/share/runtime/handles.cpp:100
#5 0x00007ffff6546e23 in HandleArea::oops_do (this=0x7ffff02e1c30, f=0x7fff7c001478) at /home/dai/jdk/src/hotspot/share/runtime/handles.cpp:108
#6 0x00007ffff6d85dd4 in Thread::oops_do_no_frames (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:550
#7 0x00007ffff6d8a513 in JavaThread::oops_do_no_frames (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:1968
#8 0x00007ffff6d85e28 in Thread::oops_do (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:580
#9 0x00007ffff6d91359 in ParallelOopsDoThreadClosure::do_thread (this=0x7fff87dfaa00, t=0x7ffff02e1160) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:3620
#10 0x00007ffff6d8c40b in Threads::possibly_parallel_threads_do (is_par=true, tc=0x7fff87dfaa00) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:2545
#11 0x00007ffff6d8eac8 in Threads::possibly_parallel_oops_do (is_par=true, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:3626
#12 0x00007ffff64dea53 in G1RootProcessor::process_java_roots (this=0x7fffc9723df0, closures=0x7fff7c001470, phase_times=0x7fffb8001380, worker_id=1)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1RootProcessor.cpp:183
#13 0x00007ffff64de78e in G1RootProcessor::evacuate_roots (this=0x7fffc9723df0, pss=0x7fff7c000d90, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1RootProcessor.cpp:60
#14 0x00007ffff64f06b8 in G1EvacuateRegionsTask::scan_roots (this=0x7fffc9723f50, pss=0x7fff7c000d90, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1YoungCollector.cpp:706
#15 0x00007ffff64f0632 in G1EvacuateRegionsBaseTask::work (this=0x7fffc9723f50, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1YoungCollector.cpp:693
#16 0x00007ffff6e8bb7c in WorkerTaskDispatcher::worker_run_task (this=0x7ffff00a4c88) at /home/dai/jdk/src/hotspot/share/gc/shared/workerThread.cpp:67
#17 0x00007ffff6e8c074 in WorkerThread::run (this=0x7fffb800df30) at /home/dai/jdk/src/hotspot/share/gc/shared/workerThread.cpp:159
#18 0x00007ffff6d8557f in Thread::call_run (this=0x7fffb800df30) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:358
#19 0x00007ffff6acc1e7 in thread_native_entry (thread=0x7fffb800df30) at /home/dai/jdk/src/hotspot/os/linux/os_linux.cpp:705
#20 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#21 0x00007ffff7d26a40 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
内存复制:
static void pd_disjoint_words(const HeapWord* from, HeapWord* to, size_t count) {
#ifdef AMD64
switch (count) {
case 8: to[7] = from[7];
case 7: to[6] = from[6];
case 6: to[5] = from[5];
case 5: to[4] = from[4];
case 4: to[3] = from[3];
case 3: to[2] = from[2];
case 2: to[1] = from[1];
case 1: to[0] = from[0];
case 0: break;
default:
(void)memcpy(to, from, count * HeapWordSize);
break;
}
#else
// Includes a zero-count check.
intx temp;
__asm__ volatile(" testl %6,%6 ;"
" jz 3f ;"
" cmpl $32,%6 ;"
" ja 2f ;"
" subl %4,%1 ;"
"1: movl (%4),%3 ;"
" movl %7,(%5,%4,1);"
" addl $4,%0 ;"
" subl $1,%2 ;"
" jnz 1b ;"
" jmp 3f ;"
"2: rep; smovl ;"
"3: nop "
: "=S" (from), "=D" (to), "=c" (count), "=r" (temp)
: "0" (from), "1" (to), "2" (count), "3" (temp)
: "memory", "cc");
#endif // AMD64
}
并发标记: D:\jdk\src\hotspot\share\gc\g1\g1ConcurrentMark.inline.hpp
inline bool G1ConcurrentMark::mark_in_bitmap(uint const worker_id, oop const obj) {
HeapRegion* const hr = _g1h->heap_region_containing(obj);
if (hr->obj_allocated_since_marking_start(obj)) {
return false;
}
// Some callers may have stale objects to mark above TAMS after humongous reclaim.
// Can't assert that this is a valid object at this point, since it might be in the process of being copied by another thread.
assert(!hr->is_continues_humongous(), "Should not try to mark object " PTR_FORMAT " in Humongous continues region %u above TAMS " PTR_FORMAT, p2i(obj), hr->hrm_index(), p2i(hr->top_at_mark_start()));
bool success = _mark_bitmap.par_mark(obj);
if (success) {
add_to_liveness(worker_id, obj, obj->size());
}
return success;
}