8075210: Refactor strong root processing in order to allow G1 to evolve separately from GenCollectedHeap

Create a G1RootProcessor and move SharedHeap root processing to GenCollectedHeap

Reviewed-by: brutisso, tschatzl, ehelin
This commit is contained in:
Mikael Gerdin 2014-12-01 15:24:56 +01:00
parent 68b1e61637
commit f5292016ee
15 changed files with 645 additions and 596 deletions

View file

@ -48,6 +48,7 @@
#include "gc_implementation/g1/g1ParScanThreadState.inline.hpp"
#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp"
#include "gc_implementation/g1/g1RemSet.inline.hpp"
#include "gc_implementation/g1/g1RootProcessor.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp"
#include "gc_implementation/g1/g1YCTypes.hpp"
#include "gc_implementation/g1/heapRegion.inline.hpp"
@ -89,18 +90,6 @@ size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0;
// apply to TLAB allocation, which is not part of this interface: it
// is done by clients of this interface.)
// Notes on implementation of parallelism in different tasks.
//
// G1ParVerifyTask uses heap_region_par_iterate() for parallelism.
// The number of GC workers is passed to heap_region_par_iterate().
// It does use run_task() which sets _n_workers in the task.
// G1ParTask executes g1_process_roots() ->
// SharedHeap::process_roots() which calls eventually to
// CardTableModRefBS::par_non_clean_card_iterate_work() which uses
// SequentialSubTasksDone. SharedHeap::process_roots() also
// directly uses SubTasksDone (_process_strong_tasks field in SharedHeap).
//
// Local to this file.
class RefineCardTableEntryClosure: public CardTableEntryClosure {
@ -1767,7 +1756,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
_is_alive_closure_stw(this),
_ref_processor_cm(NULL),
_ref_processor_stw(NULL),
_process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)),
_bot_shared(NULL),
_evac_failure_scan_stack(NULL),
_mark_in_progress(false),
@ -1801,9 +1789,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
_gc_tracer_cm(new (ResourceObj::C_HEAP, mtGC) G1OldTracer()) {
_g1h = this;
if (_process_strong_tasks == NULL || !_process_strong_tasks->valid()) {
vm_exit_during_initialization("Failed necessary allocation.");
}
_allocator = G1Allocator::create_allocator(_g1h);
_humongous_object_threshold_in_words = HeapRegion::GrainWords / 2;
@ -3107,11 +3092,12 @@ void G1CollectedHeap::verify(bool silent, VerifyOption vo) {
G1VerifyCodeRootOopClosure codeRootsCl(this, &rootsCl, vo);
G1VerifyCodeRootBlobClosure blobsCl(&codeRootsCl);
process_all_roots(true, // activate StrongRootsScope
SO_AllCodeCache, // roots scanning options
&rootsCl,
&cldCl,
&blobsCl);
{
G1RootProcessor root_processor(this);
root_processor.process_all_roots(&rootsCl,
&cldCl,
&blobsCl);
}
bool failures = rootsCl.failures() || codeRootsCl.failures();
@ -4360,60 +4346,11 @@ class G1KlassScanClosure : public KlassClosure {
}
};
class G1CodeBlobClosure : public CodeBlobClosure {
class HeapRegionGatheringOopClosure : public OopClosure {
G1CollectedHeap* _g1h;
OopClosure* _work;
nmethod* _nm;
template <typename T>
void do_oop_work(T* p) {
_work->do_oop(p);
T oop_or_narrowoop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(oop_or_narrowoop)) {
oop o = oopDesc::decode_heap_oop_not_null(oop_or_narrowoop);
HeapRegion* hr = _g1h->heap_region_containing_raw(o);
assert(!_g1h->obj_in_cs(o) || hr->rem_set()->strong_code_roots_list_contains(_nm), "if o still in CS then evacuation failed and nm must already be in the remset");
hr->add_strong_code_root(_nm);
}
}
public:
HeapRegionGatheringOopClosure(OopClosure* oc) : _g1h(G1CollectedHeap::heap()), _work(oc), _nm(NULL) {}
void do_oop(oop* o) {
do_oop_work(o);
}
void do_oop(narrowOop* o) {
do_oop_work(o);
}
void set_nm(nmethod* nm) {
_nm = nm;
}
};
HeapRegionGatheringOopClosure _oc;
public:
G1CodeBlobClosure(OopClosure* oc) : _oc(oc) {}
void do_code_blob(CodeBlob* cb) {
nmethod* nm = cb->as_nmethod_or_null();
if (nm != NULL) {
if (!nm->test_set_oops_do_mark()) {
_oc.set_nm(nm);
nm->oops_do(&_oc);
nm->fix_oop_relocations();
}
}
}
};
class G1ParTask : public AbstractGangTask {
protected:
G1CollectedHeap* _g1h;
RefToScanQueueSet *_queues;
G1RootProcessor* _root_processor;
ParallelTaskTerminator _terminator;
uint _n_workers;
@ -4421,10 +4358,11 @@ protected:
Mutex* stats_lock() { return &_stats_lock; }
public:
G1ParTask(G1CollectedHeap* g1h, RefToScanQueueSet *task_queues)
G1ParTask(G1CollectedHeap* g1h, RefToScanQueueSet *task_queues, G1RootProcessor* root_processor)
: AbstractGangTask("G1 collection"),
_g1h(g1h),
_queues(task_queues),
_root_processor(root_processor),
_terminator(0, _queues),
_stats_lock(Mutex::leaf, "parallel G1 stats lock", true)
{}
@ -4438,13 +4376,7 @@ public:
ParallelTaskTerminator* terminator() { return &_terminator; }
virtual void set_for_termination(int active_workers) {
// This task calls set_n_termination() in par_non_clean_card_iterate_work()
// in the young space (_par_seq_tasks) in the G1 heap
// for SequentialSubTasksDone.
// This task also uses SubTasksDone in SharedHeap and G1CollectedHeap
// both of which need setting by set_n_termination().
_g1h->SharedHeap::set_n_termination(active_workers);
_g1h->set_n_termination(active_workers);
_root_processor->set_num_workers(active_workers);
terminator()->reset_for_reuse(active_workers);
_n_workers = active_workers;
}
@ -4513,24 +4445,21 @@ public:
false, // Process all klasses.
true); // Need to claim CLDs.
G1CodeBlobClosure scan_only_code_cl(&scan_only_root_cl);
G1CodeBlobClosure scan_mark_code_cl(&scan_mark_root_cl);
// IM Weak code roots are handled later.
OopClosure* strong_root_cl;
OopClosure* weak_root_cl;
CLDClosure* strong_cld_cl;
CLDClosure* weak_cld_cl;
CodeBlobClosure* strong_code_cl;
bool trace_metadata = false;
if (_g1h->g1_policy()->during_initial_mark_pause()) {
// We also need to mark copied objects.
strong_root_cl = &scan_mark_root_cl;
strong_cld_cl = &scan_mark_cld_cl;
strong_code_cl = &scan_mark_code_cl;
if (ClassUnloadingWithConcurrentMark) {
weak_root_cl = &scan_mark_weak_root_cl;
weak_cld_cl = &scan_mark_weak_cld_cl;
trace_metadata = true;
} else {
weak_root_cl = &scan_mark_root_cl;
weak_cld_cl = &scan_mark_cld_cl;
@ -4540,21 +4469,21 @@ public:
weak_root_cl = &scan_only_root_cl;
strong_cld_cl = &scan_only_cld_cl;
weak_cld_cl = &scan_only_cld_cl;
strong_code_cl = &scan_only_code_cl;
}
G1ParPushHeapRSClosure push_heap_rs_cl(_g1h, &pss);
pss.start_strong_roots();
_g1h->g1_process_roots(strong_root_cl,
weak_root_cl,
&push_heap_rs_cl,
strong_cld_cl,
weak_cld_cl,
strong_code_cl,
worker_id);
_root_processor->evacuate_roots(strong_root_cl,
weak_root_cl,
strong_cld_cl,
weak_cld_cl,
trace_metadata,
worker_id);
G1ParPushHeapRSClosure push_heap_rs_cl(_g1h, &pss);
_root_processor->scan_remembered_sets(&push_heap_rs_cl,
weak_root_cl,
worker_id);
pss.end_strong_roots();
{
@ -4585,87 +4514,6 @@ public:
}
};
// *** Common G1 Evacuation Stuff
// This method is run in a GC worker.
void
G1CollectedHeap::
g1_process_roots(OopClosure* scan_non_heap_roots,
OopClosure* scan_non_heap_weak_roots,
G1ParPushHeapRSClosure* 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_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, obj_copy_time_sec);
double ext_root_time_sec = os::elapsedTime() - ext_roots_start - obj_copy_time_sec;
g1_policy()->phase_times()->record_time_secs(G1GCPhaseTimes::ExtRootScan, worker_i, ext_root_time_sec);
// 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).
{
G1GCParPhaseTimesTracker x(g1_policy()->phase_times(), G1GCPhaseTimes::SATBFiltering, worker_i);
if (!_process_strong_tasks->is_task_claimed(G1H_PS_filter_satb_buffers) && mark_in_progress()) {
JavaThread::satb_mark_queue_set().filter_thread_buffers();
}
}
// 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();
}
class G1StringSymbolTableUnlinkTask : public AbstractGangTask {
private:
BoolObjectClosure* _is_alive;
@ -5612,7 +5460,6 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
workers()->set_active_workers(n_workers);
set_par_threads(n_workers);
G1ParTask g1_par_task(this, _task_queues);
init_for_evac_failure(NULL);
@ -5621,7 +5468,8 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
double end_par_time_sec;
{
StrongRootsScope srs(this);
G1RootProcessor root_processor(this);
G1ParTask g1_par_task(this, _task_queues, &root_processor);
// InitialMark needs claim bits to keep track of the marked-through CLDs.
if (g1_policy()->during_initial_mark_pause()) {
ClassLoaderDataGraph::clear_claimed_marks();
@ -5637,9 +5485,9 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
end_par_time_sec = os::elapsedTime();
// Closing the inner scope will execute the destructor
// for the StrongRootsScope object. We record the current
// for the G1RootProcessor object. We record the current
// elapsed time before closing the scope so that time
// taken for the SRS destructor is NOT included in the
// taken for the destructor is NOT included in the
// reported parallel time.
}