提交 87e611ef 编写于 作者: T tongbao.ztb 提交者: 云矅

[Misc] print the detail time when processing the gc roots

Summary:
- port D64010 to dragonwell
- print the detail time when doing root processing in generational heap collection

Test Plan:
hotspot/test/gc/TestPrintGCRootsTraceTime.java
hotspot/test/gc/

Reviewers: 传胜

Differential Revision: https://aone.alibaba-inc.com/code/D851700
上级 0dfd4203
...@@ -3058,6 +3058,8 @@ void CMSCollector::verify_after_remark_work_1() { ...@@ -3058,6 +3058,8 @@ void CMSCollector::verify_after_remark_work_1() {
HandleMark hm; HandleMark hm;
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
// Get a clear set of claim bits for the roots processing to work with. // Get a clear set of claim bits for the roots processing to work with.
ClassLoaderDataGraph::clear_claimed_marks(); ClassLoaderDataGraph::clear_claimed_marks();
...@@ -3065,6 +3067,8 @@ void CMSCollector::verify_after_remark_work_1() { ...@@ -3065,6 +3067,8 @@ void CMSCollector::verify_after_remark_work_1() {
MarkRefsIntoClosure notOlder(_span, verification_mark_bm()); MarkRefsIntoClosure notOlder(_span, verification_mark_bm());
gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(_cmsGen->level(), gch->gen_process_roots(_cmsGen->level(),
true, // younger gens are roots true, // younger gens are roots
true, // activate StrongRootsScope true, // activate StrongRootsScope
...@@ -3072,7 +3076,10 @@ void CMSCollector::verify_after_remark_work_1() { ...@@ -3072,7 +3076,10 @@ void CMSCollector::verify_after_remark_work_1() {
should_unload_classes(), should_unload_classes(),
&notOlder, &notOlder,
NULL, NULL,
NULL); // SSS: Provide correct closure NULL, // SSS: Provide correct closure
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
// Now mark from the roots // Now mark from the roots
MarkFromRootsClosure markFromRootsClosure(this, _span, MarkFromRootsClosure markFromRootsClosure(this, _span,
...@@ -3123,6 +3130,7 @@ void CMSCollector::verify_after_remark_work_2() { ...@@ -3123,6 +3130,7 @@ void CMSCollector::verify_after_remark_work_2() {
HandleMark hm; HandleMark hm;
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
// Get a clear set of claim bits for the roots processing to work with. // Get a clear set of claim bits for the roots processing to work with.
ClassLoaderDataGraph::clear_claimed_marks(); ClassLoaderDataGraph::clear_claimed_marks();
...@@ -3133,6 +3141,8 @@ void CMSCollector::verify_after_remark_work_2() { ...@@ -3133,6 +3141,8 @@ void CMSCollector::verify_after_remark_work_2() {
gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(_cmsGen->level(), gch->gen_process_roots(_cmsGen->level(),
true, // younger gens are roots true, // younger gens are roots
true, // activate StrongRootsScope true, // activate StrongRootsScope
...@@ -3140,8 +3150,10 @@ void CMSCollector::verify_after_remark_work_2() { ...@@ -3140,8 +3150,10 @@ void CMSCollector::verify_after_remark_work_2() {
should_unload_classes(), should_unload_classes(),
&notOlder, &notOlder,
NULL, NULL,
&cld_closure); &cld_closure,
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
// Now mark from the roots // Now mark from the roots
MarkFromRootsVerifyClosure markFromRootsClosure(this, _span, MarkFromRootsVerifyClosure markFromRootsClosure(this, _span,
verification_mark_bm(), markBitMap(), verification_mark_stack()); verification_mark_bm(), markBitMap(), verification_mark_stack());
...@@ -3715,6 +3727,7 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) { ...@@ -3715,6 +3727,7 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) {
// Update the saved marks which may affect the root scans. // Update the saved marks which may affect the root scans.
gch->save_marks(); gch->save_marks();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
// weak reference processing has not started yet. // weak reference processing has not started yet.
ref_processor()->set_enqueuing_is_done(false); ref_processor()->set_enqueuing_is_done(false);
...@@ -3737,6 +3750,7 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) { ...@@ -3737,6 +3750,7 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) {
FlexibleWorkGang* workers = gch->workers(); FlexibleWorkGang* workers = gch->workers();
assert(workers != NULL, "Need parallel worker threads."); assert(workers != NULL, "Need parallel worker threads.");
int n_workers = workers->active_workers(); int n_workers = workers->active_workers();
phase_times->note_gc_start(n_workers);
CMSParInitialMarkTask tsk(this, n_workers); CMSParInitialMarkTask tsk(this, n_workers);
gch->set_par_threads(n_workers); gch->set_par_threads(n_workers);
initialize_sequential_subtasks_for_young_gen_rescan(n_workers); initialize_sequential_subtasks_for_young_gen_rescan(n_workers);
...@@ -3752,6 +3766,8 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) { ...@@ -3752,6 +3766,8 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) {
// The serial version. // The serial version.
CLDToOopClosure cld_closure(&notOlder, true); CLDToOopClosure cld_closure(&notOlder, true);
gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(_cmsGen->level(), gch->gen_process_roots(_cmsGen->level(),
true, // younger gens are roots true, // younger gens are roots
true, // activate StrongRootsScope true, // activate StrongRootsScope
...@@ -3759,7 +3775,14 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) { ...@@ -3759,7 +3775,14 @@ void CMSCollector::checkpointRootsInitialWork(bool asynch) {
should_unload_classes(), should_unload_classes(),
&notOlder, &notOlder,
NULL, NULL,
&cld_closure); &cld_closure,
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
}
phase_times->note_gc_end();
if (PrintGCRootsTraceTime && PrintGCDetails) {
phase_times->log_gc_details();
} }
} }
...@@ -5234,6 +5257,8 @@ void CMSParInitialMarkTask::work(uint worker_id) { ...@@ -5234,6 +5257,8 @@ void CMSParInitialMarkTask::work(uint worker_id) {
// ---------- scan from roots -------------- // ---------- scan from roots --------------
_timer.start(); _timer.start();
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, worker_id, os::elapsedTime());
Par_MarkRefsIntoClosure par_mri_cl(_collector->_span, &(_collector->_markBitMap)); Par_MarkRefsIntoClosure par_mri_cl(_collector->_span, &(_collector->_markBitMap));
// ---------- young gen roots -------------- // ---------- young gen roots --------------
...@@ -5260,7 +5285,9 @@ void CMSParInitialMarkTask::work(uint worker_id) { ...@@ -5260,7 +5285,9 @@ void CMSParInitialMarkTask::work(uint worker_id) {
_collector->should_unload_classes(), _collector->should_unload_classes(),
&par_mri_cl, &par_mri_cl,
NULL, NULL,
&cld_closure); &cld_closure,
phase_times,
worker_id);
assert(_collector->should_unload_classes() assert(_collector->should_unload_classes()
|| (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache), || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
"if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops"); "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops");
...@@ -5270,6 +5297,7 @@ void CMSParInitialMarkTask::work(uint worker_id) { ...@@ -5270,6 +5297,7 @@ void CMSParInitialMarkTask::work(uint worker_id) {
"Finished remaining root initial mark scan work in %dth thread: %3.3f sec", "Finished remaining root initial mark scan work in %dth thread: %3.3f sec",
worker_id, _timer.seconds()); worker_id, _timer.seconds());
} }
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, worker_id, os::elapsedTime());
} }
// Parallel remark task // Parallel remark task
...@@ -5366,6 +5394,8 @@ void CMSParRemarkTask::work(uint worker_id) { ...@@ -5366,6 +5394,8 @@ void CMSParRemarkTask::work(uint worker_id) {
// ---------- rescan from roots -------------- // ---------- rescan from roots --------------
_timer.start(); _timer.start();
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, worker_id, os::elapsedTime());
Par_MarkRefsIntoAndScanClosure par_mrias_cl(_collector, Par_MarkRefsIntoAndScanClosure par_mrias_cl(_collector,
_collector->_span, _collector->ref_processor(), _collector->_span, _collector->ref_processor(),
&(_collector->_markBitMap), &(_collector->_markBitMap),
...@@ -5396,7 +5426,9 @@ void CMSParRemarkTask::work(uint worker_id) { ...@@ -5396,7 +5426,9 @@ void CMSParRemarkTask::work(uint worker_id) {
_collector->should_unload_classes(), _collector->should_unload_classes(),
&par_mrias_cl, &par_mrias_cl,
NULL, NULL,
NULL); // The dirty klasses will be handled below NULL, // The dirty klasses will be handled below
phase_times,
worker_id);
assert(_collector->should_unload_classes() assert(_collector->should_unload_classes()
|| (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache), || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
...@@ -5481,6 +5513,7 @@ void CMSParRemarkTask::work(uint worker_id) { ...@@ -5481,6 +5513,7 @@ void CMSParRemarkTask::work(uint worker_id) {
"Finished work stealing in %dth thread: %3.3f sec", "Finished work stealing in %dth thread: %3.3f sec",
worker_id, _timer.seconds()); worker_id, _timer.seconds());
} }
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, worker_id, os::elapsedTime());
} }
// Note that parameter "i" is not used. // Note that parameter "i" is not used.
...@@ -5898,6 +5931,8 @@ void CMSCollector::do_remark_parallel() { ...@@ -5898,6 +5931,8 @@ void CMSCollector::do_remark_parallel() {
// claimed by the parallel threads. // claimed by the parallel threads.
cms_space->initialize_sequential_subtasks_for_rescan(n_workers); cms_space->initialize_sequential_subtasks_for_rescan(n_workers);
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
phase_times->note_gc_start(n_workers);
// It turns out that even when we're using 1 thread, doing the work in a // It turns out that even when we're using 1 thread, doing the work in a
// separate thread causes wide variance in run times. We can't help this // separate thread causes wide variance in run times. We can't help this
// in the multi-threaded case, but we special-case n=1 here to get // in the multi-threaded case, but we special-case n=1 here to get
...@@ -5915,6 +5950,11 @@ void CMSCollector::do_remark_parallel() { ...@@ -5915,6 +5950,11 @@ void CMSCollector::do_remark_parallel() {
tsk.work(0); tsk.work(0);
} }
phase_times->note_gc_end();
if (PrintGCRootsTraceTime && PrintGCDetails) {
phase_times->log_gc_details();
}
gch->set_par_threads(0); // 0 ==> non-parallel. gch->set_par_threads(0); // 0 ==> non-parallel.
// restore, single-threaded for now, any preserved marks // restore, single-threaded for now, any preserved marks
// as a result of work_q overflow // as a result of work_q overflow
...@@ -5927,7 +5967,7 @@ void CMSCollector::do_remark_non_parallel() { ...@@ -5927,7 +5967,7 @@ void CMSCollector::do_remark_non_parallel() {
HandleMark hm; HandleMark hm;
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
ReferenceProcessorMTDiscoveryMutator mt(ref_processor(), false); ReferenceProcessorMTDiscoveryMutator mt(ref_processor(), false);
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
MarkRefsIntoAndScanClosure MarkRefsIntoAndScanClosure
mrias_cl(_span, ref_processor(), &_markBitMap, NULL /* not precleaning */, mrias_cl(_span, ref_processor(), &_markBitMap, NULL /* not precleaning */,
&_markStack, this, &_markStack, this,
...@@ -5981,6 +6021,8 @@ void CMSCollector::do_remark_non_parallel() { ...@@ -5981,6 +6021,8 @@ void CMSCollector::do_remark_non_parallel() {
gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
GenCollectedHeap::StrongRootsScope srs(gch); GenCollectedHeap::StrongRootsScope srs(gch);
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(_cmsGen->level(), gch->gen_process_roots(_cmsGen->level(),
true, // younger gens as roots true, // younger gens as roots
false, // use the local StrongRootsScope false, // use the local StrongRootsScope
...@@ -5988,7 +6030,10 @@ void CMSCollector::do_remark_non_parallel() { ...@@ -5988,7 +6030,10 @@ void CMSCollector::do_remark_non_parallel() {
should_unload_classes(), should_unload_classes(),
&mrias_cl, &mrias_cl,
NULL, NULL,
NULL); // The dirty klasses will be handled below NULL, // The dirty klasses will be handled below
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
assert(should_unload_classes() assert(should_unload_classes()
|| (roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache), || (roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
......
/* /*
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -27,223 +27,10 @@ ...@@ -27,223 +27,10 @@
#include "gc_implementation/g1/g1GCPhaseTimes.hpp" #include "gc_implementation/g1/g1GCPhaseTimes.hpp"
#include "gc_implementation/g1/g1Log.hpp" #include "gc_implementation/g1/g1Log.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp" #include "gc_implementation/g1/g1StringDedup.hpp"
#include "gc_implementation/shared/workerDataArray.inline.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
// Helper class for avoiding interleaved logging
class LineBuffer: public StackObj {
private:
static const int BUFFER_LEN = 1024;
static const int INDENT_CHARS = 3;
char _buffer[BUFFER_LEN];
int _indent_level;
int _cur;
void vappend(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) {
int res = os::vsnprintf(&_buffer[_cur], BUFFER_LEN - _cur, format, ap);
if (res > BUFFER_LEN) {
DEBUG_ONLY(warning("buffer too small in LineBuffer");)
_buffer[BUFFER_LEN -1] = 0;
_cur = BUFFER_LEN; // vsnprintf above should not add to _buffer if we are called again
} else if (res != -1) {
_cur += res;
}
}
public:
explicit LineBuffer(int indent_level): _indent_level(indent_level), _cur(0) {
for (; (_cur < BUFFER_LEN && _cur < (_indent_level * INDENT_CHARS)); _cur++) {
_buffer[_cur] = ' ';
}
}
#ifndef PRODUCT
~LineBuffer() {
assert(_cur == _indent_level * INDENT_CHARS, "pending data in buffer - append_and_print_cr() not called?");
}
#endif
void append(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) {
va_list ap;
va_start(ap, format);
vappend(format, ap);
va_end(ap);
}
void print_cr() {
gclog_or_tty->print_cr("%s", _buffer);
_cur = _indent_level * INDENT_CHARS;
}
void append_and_print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) {
va_list ap;
va_start(ap, format);
vappend(format, ap);
va_end(ap);
print_cr();
}
};
template <class T>
class WorkerDataArray : public CHeapObj<mtGC> {
friend class G1GCParPhasePrinter;
T* _data;
uint _length;
const char* _title;
bool _print_sum;
int _log_level;
uint _indent_level;
bool _enabled;
WorkerDataArray<size_t>* _thread_work_items;
NOT_PRODUCT(T uninitialized();)
// We are caching the sum and average to only have to calculate them once.
// This is not done in an MT-safe way. It is intended to allow single
// threaded code to call sum() and average() multiple times in any order
// without having to worry about the cost.
bool _has_new_data;
T _sum;
T _min;
T _max;
double _average;
public:
WorkerDataArray(uint length, const char* title, bool print_sum, int log_level, uint indent_level) :
_title(title), _length(0), _print_sum(print_sum), _log_level(log_level), _indent_level(indent_level),
_has_new_data(true), _thread_work_items(NULL), _enabled(true) {
assert(length > 0, "Must have some workers to store data for");
_length = length;
_data = NEW_C_HEAP_ARRAY(T, _length, mtGC);
}
~WorkerDataArray() {
FREE_C_HEAP_ARRAY(T, _data, mtGC);
}
void link_thread_work_items(WorkerDataArray<size_t>* thread_work_items) {
_thread_work_items = thread_work_items;
}
WorkerDataArray<size_t>* thread_work_items() { return _thread_work_items; }
void set(uint worker_i, T value) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] == WorkerDataArray<T>::uninitialized(), err_msg("Overwriting data for worker %d in %s", worker_i, _title));
_data[worker_i] = value;
_has_new_data = true;
}
void set_thread_work_item(uint worker_i, size_t value) {
assert(_thread_work_items != NULL, "No sub count");
_thread_work_items->set(worker_i, value);
}
T get(uint worker_i) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] != WorkerDataArray<T>::uninitialized(), err_msg("No data added for worker %d", worker_i));
return _data[worker_i];
}
void add(uint worker_i, T value) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] != WorkerDataArray<T>::uninitialized(), err_msg("No data to add to for worker %d", worker_i));
_data[worker_i] += value;
_has_new_data = true;
}
double average(uint active_threads){
calculate_totals(active_threads);
return _average;
}
T sum(uint active_threads) {
calculate_totals(active_threads);
return _sum;
}
T minimum(uint active_threads) {
calculate_totals(active_threads);
return _min;
}
T maximum(uint active_threads) {
calculate_totals(active_threads);
return _max;
}
void reset() PRODUCT_RETURN;
void verify(uint active_threads) PRODUCT_RETURN;
void set_enabled(bool enabled) { _enabled = enabled; }
int log_level() { return _log_level; }
private:
void calculate_totals(uint active_threads){
if (!_has_new_data) {
return;
}
_sum = (T)0;
_min = _data[0];
_max = _min;
assert(active_threads <= _length, "Wrong number of active threads");
for (uint i = 0; i < active_threads; ++i) {
T val = _data[i];
_sum += val;
_min = MIN2(_min, val);
_max = MAX2(_max, val);
}
_average = (double)_sum / (double)active_threads;
_has_new_data = false;
}
};
#ifndef PRODUCT
template <>
size_t WorkerDataArray<size_t>::uninitialized() {
return (size_t)-1;
}
template <>
double WorkerDataArray<double>::uninitialized() {
return -1.0;
}
template <class T>
void WorkerDataArray<T>::reset() {
for (uint i = 0; i < _length; i++) {
_data[i] = WorkerDataArray<T>::uninitialized();
}
if (_thread_work_items != NULL) {
_thread_work_items->reset();
}
}
template <class T>
void WorkerDataArray<T>::verify(uint active_threads) {
if (!_enabled) {
return;
}
assert(active_threads <= _length, "Wrong number of active threads");
for (uint i = 0; i < active_threads; i++) {
assert(_data[i] != WorkerDataArray<T>::uninitialized(),
err_msg("Invalid data for worker %u in '%s'", i, _title));
}
if (_thread_work_items != NULL) {
_thread_work_items->verify(active_threads);
}
}
#endif
G1GCPhaseTimes::G1GCPhaseTimes(uint max_gc_threads) : G1GCPhaseTimes::G1GCPhaseTimes(uint max_gc_threads) :
_max_gc_threads(max_gc_threads) _max_gc_threads(max_gc_threads)
{ {
......
/* /*
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMESLOG_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMESLOG_HPP
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "gc_implementation/shared/workerDataArray.hpp"
class LineBuffer; class LineBuffer;
......
...@@ -598,6 +598,9 @@ void ParNewGenTask::set_for_termination(int active_workers) { ...@@ -598,6 +598,9 @@ void ParNewGenTask::set_for_termination(int active_workers) {
void ParNewGenTask::work(uint worker_id) { void ParNewGenTask::work(uint worker_id) {
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, worker_id, os::elapsedTime());
// Since this is being done in a separate thread, need new resource // Since this is being done in a separate thread, need new resource
// and handle marks. // and handle marks.
ResourceMark rm; ResourceMark rm;
...@@ -627,12 +630,15 @@ void ParNewGenTask::work(uint worker_id) { ...@@ -627,12 +630,15 @@ void ParNewGenTask::work(uint worker_id) {
GenCollectedHeap::StrongAndWeakRoots, GenCollectedHeap::StrongAndWeakRoots,
&par_scan_state.to_space_root_closure(), &par_scan_state.to_space_root_closure(),
&par_scan_state.older_gen_closure(), &par_scan_state.older_gen_closure(),
&cld_scan_closure); &cld_scan_closure,
phase_times,
worker_id);
par_scan_state.end_strong_roots(); par_scan_state.end_strong_roots();
// "evacuate followers". // "evacuate followers".
par_scan_state.evacuate_followers_closure().do_void(); par_scan_state.evacuate_followers_closure().do_void();
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, worker_id, os::elapsedTime());
} }
#ifdef _MSC_VER #ifdef _MSC_VER
...@@ -919,6 +925,7 @@ void ParNewGeneration::collect(bool full, ...@@ -919,6 +925,7 @@ void ParNewGeneration::collect(bool full,
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
_gc_timer->register_gc_start(); _gc_timer->register_gc_start();
assert(gch->kind() == CollectedHeap::GenCollectedHeap, assert(gch->kind() == CollectedHeap::GenCollectedHeap,
...@@ -931,6 +938,7 @@ void ParNewGeneration::collect(bool full, ...@@ -931,6 +938,7 @@ void ParNewGeneration::collect(bool full,
workers->active_workers(), workers->active_workers(),
Threads::number_of_non_daemon_threads()); Threads::number_of_non_daemon_threads());
workers->set_active_workers(active_workers); workers->set_active_workers(active_workers);
phase_times->note_gc_start(active_workers);
assert(gch->n_gens() == 2, assert(gch->n_gens() == 2,
"Par collection currently only works with single older gen."); "Par collection currently only works with single older gen.");
_next_gen = gch->next_gen(this); _next_gen = gch->next_gen(this);
...@@ -999,6 +1007,10 @@ void ParNewGeneration::collect(bool full, ...@@ -999,6 +1007,10 @@ void ParNewGeneration::collect(bool full,
thread_state_set.reset(0 /* Bad value in debug if not reset */, thread_state_set.reset(0 /* Bad value in debug if not reset */,
promotion_failed()); promotion_failed());
phase_times->note_gc_end();
if (PrintGCRootsTraceTime && PrintGCDetails) {
phase_times->log_gc_details();
}
// Process (weak) reference objects found during scavenge. // Process (weak) reference objects found during scavenge.
ReferenceProcessor* rp = ref_processor(); ReferenceProcessor* rp = ref_processor();
IsAliveClosure is_alive(this); IsAliveClosure is_alive(this);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTimer.hpp"
#include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcTrace.hpp"
#include "gc_implementation/shared/gcTraceTime.hpp" #include "gc_implementation/shared/gcTraceTime.hpp"
#include "gc_implementation/shared/workerDataArray.inline.hpp"
#include "runtime/globals.hpp" #include "runtime/globals.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
...@@ -34,7 +35,6 @@ ...@@ -34,7 +35,6 @@
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
#include "utilities/ticks.inline.hpp" #include "utilities/ticks.inline.hpp"
GCTraceTime::GCTraceTime(const char* title, bool doit, bool print_cr, GCTimer* timer, GCId gc_id) : GCTraceTime::GCTraceTime(const char* title, bool doit, bool print_cr, GCTimer* timer, GCId gc_id) :
_title(title), _doit(doit), _print_cr(print_cr), _timer(timer), _start_counter() { _title(title), _doit(doit), _print_cr(print_cr), _timer(timer), _start_counter() {
if (_doit || _timer != NULL) { if (_doit || _timer != NULL) {
...@@ -82,3 +82,205 @@ GCTraceTime::~GCTraceTime() { ...@@ -82,3 +82,205 @@ GCTraceTime::~GCTraceTime() {
gclog_or_tty->flush(); gclog_or_tty->flush();
} }
} }
//========================================================================
// GenGC Log level class
class GenGCLog : public AllStatic {
public:
typedef enum {
LevelNone,
LevelFine,
LevelFinest,
} LogLevel;
private:
static LogLevel _level;
public:
inline static bool fine() {
return _level >= LevelFine;
}
inline static bool finest() {
return _level == LevelFinest;
}
static LogLevel level() {
return _level;
}
};
GenGCLog::LogLevel GenGCLog::_level = GenGCLog::LevelNone;
GenGCPhaseTimes::GenGCPhaseTimes(uint max_gc_threads) :
_max_gc_threads(max_gc_threads)
{
assert(max_gc_threads > 0, "Must have some GC threads");
_gc_par_phases[GCWorkerStart] = new WorkerDataArray<double>(max_gc_threads, "GC Worker Start (ms)", false, GenGCLog::LevelFine, 2);
_gc_par_phases[RootProcess] = new WorkerDataArray<double>(max_gc_threads, "Root Processing (ms)", true, GenGCLog::LevelFine, 2);
// Root scanning phases
_gc_par_phases[ThreadRoots] = new WorkerDataArray<double>(max_gc_threads, "Thread Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[StringTableRoots] = new WorkerDataArray<double>(max_gc_threads, "StringTable Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[UniverseRoots] = new WorkerDataArray<double>(max_gc_threads, "Universe Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[JNIRoots] = new WorkerDataArray<double>(max_gc_threads, "JNI Handles Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[ObjectSynchronizerRoots] = new WorkerDataArray<double>(max_gc_threads, "ObjectSynchronizer Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[FlatProfilerRoots] = new WorkerDataArray<double>(max_gc_threads, "FlatProfiler Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[ManagementRoots] = new WorkerDataArray<double>(max_gc_threads, "Management Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[SystemDictionaryRoots] = new WorkerDataArray<double>(max_gc_threads, "SystemDictionary Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[CLDGRoots] = new WorkerDataArray<double>(max_gc_threads, "CLDG Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[JVMTIRoots] = new WorkerDataArray<double>(max_gc_threads, "JVMTI Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[CodeCacheRoots] = new WorkerDataArray<double>(max_gc_threads, "CodeCache Roots (ms)", true, GenGCLog::LevelFinest, 3);
_gc_par_phases[OldGenScan] = new WorkerDataArray<double>(max_gc_threads, "older-gen scanning (ms)", true, GenGCLog::LevelFine, 3);
_gc_par_phases[Other] = new WorkerDataArray<double>(max_gc_threads, "GC Worker Other (ms)", true, GenGCLog::LevelFine, 2);
_gc_par_phases[GCWorkerTotal] = new WorkerDataArray<double>(max_gc_threads, "GC Worker Total (ms)", true, GenGCLog::LevelFine, 2);
_gc_par_phases[GCWorkerEnd] = new WorkerDataArray<double>(max_gc_threads, "GC Worker End (ms)", false, GenGCLog::LevelFine, 2);
}
void GenGCPhaseTimes::note_gc_start(uint active_gc_threads) {
assert(active_gc_threads > 0, "The number of threads must be > 0");
assert(active_gc_threads <= _max_gc_threads, "The number of active threads must be <= the max number of threads");
_active_gc_threads = active_gc_threads;
for (int i = 0; i < GCParPhasesSentinel; i++) {
_gc_par_phases[i]->reset();
}
}
void GenGCPhaseTimes::note_gc_end() {
for (uint i = 0; i < _active_gc_threads; i++) {
double worker_time = _gc_par_phases[GCWorkerEnd]->get(i) - _gc_par_phases[GCWorkerStart]->get(i);
record_time_secs(GCWorkerTotal, i , worker_time);
double worker_known_time = _gc_par_phases[RootProcess]->get(i);
record_time_secs(Other, i, worker_time - worker_known_time);
}
for (int i = 0; i < GCParPhasesSentinel; i++) {
_gc_par_phases[i]->verify(_active_gc_threads);
}
}
void GenGCPhaseTimes::print_stats(int level, const char* str, double value) {
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
}
void GenGCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) {
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: %u]", str, value, workers);
}
// record the time a phase took in seconds
void GenGCPhaseTimes::record_time_secs(GCParPhases phase, uint worker_i, double secs) {
_gc_par_phases[phase]->set(worker_i, secs);
}
// add a number of seconds to a phase
void GenGCPhaseTimes::add_time_secs(GCParPhases phase, uint worker_i, double secs) {
_gc_par_phases[phase]->add(worker_i, secs);
}
// return the average time for a phase in milliseconds
double GenGCPhaseTimes::average_time_ms(GCParPhases phase) {
return _gc_par_phases[phase]->average(_active_gc_threads) * 1000.0;
}
double GenGCPhaseTimes::get_time_ms(GCParPhases phase, uint worker_i) {
return _gc_par_phases[phase]->get(worker_i) * 1000.0;
}
double GenGCPhaseTimes::sum_time_ms(GCParPhases phase) {
return _gc_par_phases[phase]->sum(_active_gc_threads) * 1000.0;
}
double GenGCPhaseTimes::min_time_ms(GCParPhases phase) {
return _gc_par_phases[phase]->minimum(_active_gc_threads) * 1000.0;
}
double GenGCPhaseTimes::max_time_ms(GCParPhases phase) {
return _gc_par_phases[phase]->maximum(_active_gc_threads) * 1000.0;
}
double GenGCPhaseTimes::average_thread_work_items(GCParPhases phase) {
assert(_gc_par_phases[phase]->thread_work_items() != NULL, "No sub count");
return _gc_par_phases[phase]->thread_work_items()->average(_active_gc_threads);
}
class GenGCParPhasePrinter : public StackObj {
GenGCPhaseTimes* _phase_times;
public:
GenGCParPhasePrinter(GenGCPhaseTimes* phase_times) : _phase_times(phase_times) {}
void print(GenGCPhaseTimes::GCParPhases phase_id) {
WorkerDataArray<double>* phase = _phase_times->_gc_par_phases[phase_id];
if (phase->_length == 1) {
print_single_length(phase_id, phase);
} else {
print_multi_length(phase_id, phase);
}
}
private:
void print_single_length(GenGCPhaseTimes::GCParPhases phase_id, WorkerDataArray<double>* phase) {
// No need for min, max, average and sum for only one worker
LineBuffer buf(phase->_indent_level);
buf.append_and_print_cr("[%s: %.1lf]", phase->_title, _phase_times->get_time_ms(phase_id, 0));
}
void print_time_values(LineBuffer& buf, GenGCPhaseTimes::GCParPhases phase_id, WorkerDataArray<double>* phase) {
uint active_length = _phase_times->_active_gc_threads;
for (uint i = 0; i < active_length; ++i) {
buf.append(" %.1lf", _phase_times->get_time_ms(phase_id, i));
}
buf.print_cr();
}
void print_multi_length(GenGCPhaseTimes::GCParPhases phase_id, WorkerDataArray<double>* phase) {
LineBuffer buf(phase->_indent_level);
buf.append("[%s:", phase->_title);
print_time_values(buf, phase_id, phase);
buf.append(" Min: %.1lf, Avg: %.1lf, Max: %.1lf, Diff: %.1lf",
_phase_times->min_time_ms(phase_id), _phase_times->average_time_ms(phase_id), _phase_times->max_time_ms(phase_id),
_phase_times->max_time_ms(phase_id) - _phase_times->min_time_ms(phase_id));
if (phase->_print_sum) {
// for things like the start and end times the sum is not
// that relevant
buf.append(", Sum: %.1lf", _phase_times->sum_time_ms(phase_id));
}
buf.append_and_print_cr("]");
}
};
void GenGCPhaseTimes::print() {
GenGCParPhasePrinter par_phase_printer(this);
for (int i = 0; i <= GCMainParPhasesLast; i++) {
par_phase_printer.print((GCParPhases) i);
}
}
void GenGCPhaseTimes::log_gc_details() {
this->print();
gclog_or_tty->flush();
}
GenGCParPhaseTimesTracker::GenGCParPhaseTimesTracker(GenGCPhaseTimes* phase_times, GenGCPhaseTimes::GCParPhases phase, uint worker_id) :
_phase_times(phase_times), _phase(phase), _worker_id(worker_id) {
if (_phase_times != NULL) {
_start_time = os::elapsedTime();
}
}
GenGCParPhaseTimesTracker::~GenGCParPhaseTimesTracker() {
if (_phase_times != NULL) {
_phase_times->record_time_secs(_phase, _worker_id, os::elapsedTime() - _start_time);
}
}
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -28,8 +28,11 @@ ...@@ -28,8 +28,11 @@
#include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcTrace.hpp"
#include "prims/jni_md.h" #include "prims/jni_md.h"
#include "utilities/ticks.hpp" #include "utilities/ticks.hpp"
#include "gc_implementation/shared/workerDataArray.hpp"
class GCTimer; class GCTimer;
class LineBuffer;
template <class T> class WorkerDataArray;
class GCTraceTime { class GCTraceTime {
const char* _title; const char* _title;
...@@ -43,4 +46,83 @@ class GCTraceTime { ...@@ -43,4 +46,83 @@ class GCTraceTime {
~GCTraceTime(); ~GCTraceTime();
}; };
class GenGCPhaseTimes : public CHeapObj<mtGC> {
friend class GenGCParPhasePrinter;
uint _active_gc_threads;
uint _max_gc_threads;
public:
enum GCParPhases {
GCWorkerStart,
RootProcess,
ThreadRoots,
StringTableRoots,
UniverseRoots,
JNIRoots,
ObjectSynchronizerRoots,
FlatProfilerRoots,
ManagementRoots,
SystemDictionaryRoots,
CLDGRoots,
CodeCacheRoots,
JVMTIRoots,
OldGenScan,
Other,
GCWorkerTotal,
GCWorkerEnd,
GCParPhasesSentinel
};
private:
// Markers for grouping the phases in the GCPhases enum above
static const int GCMainParPhasesLast = GCWorkerEnd;
WorkerDataArray<double>* _gc_par_phases[GCParPhasesSentinel];
double _cur_collection_par_time_ms;
// Helper methods for detailed logging
void print_stats(int level, const char* str, double value);
void print_stats(int level, const char* str, double value, uint workers);
public:
GenGCPhaseTimes(uint max_gc_threads);
void note_gc_start(uint active_gc_threads);
void note_gc_end();
void print();
void log_gc_details();
// record the time a phase took in seconds
void record_time_secs(GCParPhases phase, uint worker_i, double secs);
// add a number of seconds to a phase
void add_time_secs(GCParPhases phase, uint worker_i, double secs);
// return the average time for a phase in milliseconds
double average_time_ms(GCParPhases phase);
private:
double get_time_ms(GCParPhases phase, uint worker_i);
double sum_time_ms(GCParPhases phase);
double min_time_ms(GCParPhases phase);
double max_time_ms(GCParPhases phase);
double average_thread_work_items(GCParPhases phase);
public:
void record_par_time(double ms) {
_cur_collection_par_time_ms = ms;
}
};
class GenGCParPhaseTimesTracker : public StackObj {
double _start_time;
GenGCPhaseTimes::GCParPhases _phase;
GenGCPhaseTimes* _phase_times;
uint _worker_id;
public:
GenGCParPhaseTimesTracker(GenGCPhaseTimes* phase_times, GenGCPhaseTimes::GCParPhases phase, uint worker_id);
~GenGCParPhaseTimesTracker();
};
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_GCTRACETIME_HPP #endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_GCTRACETIME_HPP
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "gc_implementation/shared/workerDataArray.inline.hpp"
#include "utilities/ostream.hpp"
#ifndef PRODUCT
template <>
size_t WorkerDataArray<size_t>::uninitialized() {
return (size_t)-1;
}
template <>
double WorkerDataArray<double>::uninitialized() {
return -1.0;
}
#endif
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_HPP
#define SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_HPP
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
// These classes (WorkerDataArray and LineBuffer) are moved here from the original gc_implementation/g1/g1GCPhaseTimes.cpp
// And can cache the data for the parallel gc workers' performance data in G1GC and ParNew + CMS GC
template <class T>
class WorkerDataArray : public CHeapObj<mtGC> {
friend class G1GCParPhasePrinter;
friend class GenGCParPhasePrinter;
T* _data;
uint _length;
const char* _title;
bool _print_sum;
int _log_level;
uint _indent_level;
bool _enabled;
WorkerDataArray<size_t>* _thread_work_items;
NOT_PRODUCT(T uninitialized();)
// We are caching the sum and average to only have to calculate them once.
// This is not done in an MT-safe way. It is intended to allow single
// threaded code to call sum() and average() multiple times in any order
// without having to worry about the cost.
bool _has_new_data;
T _sum;
T _min;
T _max;
double _average;
public:
WorkerDataArray(uint length, const char* title, bool print_sum, int log_level, uint indent_level);
~WorkerDataArray();
void link_thread_work_items(WorkerDataArray<size_t>* thread_work_items);
WorkerDataArray<size_t>* thread_work_items() { return _thread_work_items; }
void set(uint worker_i, T value);
void set_thread_work_item(uint worker_i, size_t value);
T get(uint worker_i);
void add(uint worker_i, T value);
double average(uint active_threads);
T sum(uint active_threads);
T minimum(uint active_threads);
T maximum(uint active_threads);
void reset() PRODUCT_RETURN;
void verify(uint active_threads) PRODUCT_RETURN;
void set_enabled(bool enabled) { _enabled = enabled; }
int log_level() { return _log_level; }
private:
void calculate_totals(uint active_threads);
};
//=========================================================================
// Helper class for avoiding interleaved logging
class LineBuffer: public StackObj {
private:
static const int BUFFER_LEN = 1024;
static const int INDENT_CHARS = 3;
char _buffer[BUFFER_LEN];
int _indent_level;
int _cur;
void vappend(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) {
int res = vsnprintf(&_buffer[_cur], BUFFER_LEN - _cur, format, ap);
if (res != -1) {
_cur += res;
} else {
DEBUG_ONLY(warning("buffer too small in LineBuffer");)
_buffer[BUFFER_LEN -1] = 0;
_cur = BUFFER_LEN; // vsnprintf above should not add to _buffer if we are called again
}
}
public:
explicit LineBuffer(int indent_level): _indent_level(indent_level), _cur(0) {
for (; (_cur < BUFFER_LEN && _cur < (_indent_level * INDENT_CHARS)); _cur++) {
_buffer[_cur] = ' ';
}
}
#ifndef PRODUCT
~LineBuffer() {
assert(_cur == _indent_level * INDENT_CHARS, "pending data in buffer - append_and_print_cr() not called?");
}
#endif
void append(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) {
va_list ap;
va_start(ap, format);
vappend(format, ap);
va_end(ap);
}
void print_cr() {
gclog_or_tty->print_cr("%s", _buffer);
_cur = _indent_level * INDENT_CHARS;
}
void append_and_print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) {
va_list ap;
va_start(ap, format);
vappend(format, ap);
va_end(ap);
print_cr();
}
};
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_HPP
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_INLINE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_INLINE_HPP
#include "gc_implementation/shared/workerDataArray.hpp"
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
#include "runtime/os.hpp"
#include "utilities/ostream.hpp"
template <class T>
WorkerDataArray<T>::WorkerDataArray(uint length, const char* title, bool print_sum, int log_level, uint indent_level):
_title(title), _length(0), _print_sum(print_sum), _log_level(log_level), _indent_level(indent_level),
_has_new_data(true), _thread_work_items(NULL), _enabled(true) {
assert(length > 0, "Must have some workers to store data for");
_length = length;
_data = NEW_C_HEAP_ARRAY(T, _length, mtGC);
}
template <class T>
WorkerDataArray<T>::~WorkerDataArray() {
FREE_C_HEAP_ARRAY(T, _data, mtGC);
}
template <class T>
void WorkerDataArray<T>::link_thread_work_items(WorkerDataArray<size_t>* thread_work_items) {
_thread_work_items = thread_work_items;
}
template <class T>
void WorkerDataArray<T>::set(uint worker_i, T value) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] == WorkerDataArray<T>::uninitialized(), err_msg("Overwriting data for worker %d in %s", worker_i, _title));
_data[worker_i] = value;
_has_new_data = true;
}
template <class T>
void WorkerDataArray<T>::set_thread_work_item(uint worker_i, size_t value) {
assert(_thread_work_items != NULL, "No sub count");
_thread_work_items->set(worker_i, value);
}
template <class T>
T WorkerDataArray<T>::get(uint worker_i) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] != WorkerDataArray<T>::uninitialized(), err_msg("No data added for worker %d", worker_i));
return _data[worker_i];
}
template <class T>
void WorkerDataArray<T>::add(uint worker_i, T value) {
assert(worker_i < _length, err_msg("Worker %d is greater than max: %d", worker_i, _length));
assert(_data[worker_i] != WorkerDataArray<T>::uninitialized(), err_msg("No data to add to for worker %d", worker_i));
_data[worker_i] += value;
_has_new_data = true;
}
template <class T>
double WorkerDataArray<T>::average(uint active_threads){
calculate_totals(active_threads);
return _average;
}
template <class T>
T WorkerDataArray<T>::sum(uint active_threads) {
calculate_totals(active_threads);
return _sum;
}
template <class T>
T WorkerDataArray<T>::minimum(uint active_threads) {
calculate_totals(active_threads);
return _min;
}
template <class T>
T WorkerDataArray<T>::maximum(uint active_threads) {
calculate_totals(active_threads);
return _max;
}
template <class T>
void WorkerDataArray<T>::calculate_totals(uint active_threads){
if (!_has_new_data) {
return;
}
_sum = (T)0;
_min = _data[0];
_max = _min;
assert(active_threads <= _length, "Wrong number of active threads");
for (uint i = 0; i < active_threads; ++i) {
T val = _data[i];
_sum += val;
_min = MIN2(_min, val);
_max = MAX2(_max, val);
}
_average = (double)_sum / (double)active_threads;
_has_new_data = false;
}
#ifndef PRODUCT
template <class T>
void WorkerDataArray<T>::reset() {
for (uint i = 0; i < _length; i++) {
_data[i] = WorkerDataArray<T>::uninitialized();
}
if (_thread_work_items != NULL) {
_thread_work_items->reset();
}
}
template <class T>
void WorkerDataArray<T>::verify(uint active_threads) {
if (!_enabled) {
return;
}
assert(active_threads <= _length, "Wrong number of active threads");
for (uint i = 0; i < active_threads; i++) {
assert(_data[i] != WorkerDataArray<T>::uninitialized(),
err_msg("Invalid data for worker %u in '%s'", i, _title));
}
if (_thread_work_items != NULL) {
_thread_work_items->verify(active_threads);
}
}
#endif // PRODUCT
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_WORKERDATAARRAY_INLINE_HPP
/* /*
* Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -200,7 +200,10 @@ GenCollectorPolicy::GenCollectorPolicy() : ...@@ -200,7 +200,10 @@ GenCollectorPolicy::GenCollectorPolicy() :
_max_gen0_size(0), _max_gen0_size(0),
_gen_alignment(0), _gen_alignment(0),
_generations(NULL) _generations(NULL)
{} {
int parallel_gc_threads = CollectedHeap::use_parallel_gc_threads() ? ParallelGCThreads : 1;
_phase_times = new GenGCPhaseTimes(parallel_gc_threads);
}
size_t GenCollectorPolicy::scale_by_NewRatio_aligned(size_t base_size) { size_t GenCollectorPolicy::scale_by_NewRatio_aligned(size_t base_size) {
return align_size_down_bounded(base_size / (NewRatio + 1), _gen_alignment); return align_size_down_bounded(base_size / (NewRatio + 1), _gen_alignment);
......
/* /*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "memory/generationSpec.hpp" #include "memory/generationSpec.hpp"
#include "memory/genRemSet.hpp" #include "memory/genRemSet.hpp"
#include "utilities/macros.hpp" #include "utilities/macros.hpp"
#include "gc_implementation/shared/gcTraceTime.hpp"
// This class (or more correctly, subtypes of this class) // This class (or more correctly, subtypes of this class)
// are used to define global garbage collector attributes. // are used to define global garbage collector attributes.
...@@ -232,6 +233,8 @@ friend class TestGenCollectorPolicy; ...@@ -232,6 +233,8 @@ friend class TestGenCollectorPolicy;
GenerationSpec **_generations; GenerationSpec **_generations;
GenGCPhaseTimes* _phase_times;
// Return true if an allocation should be attempted in the older // Return true if an allocation should be attempted in the older
// generation if it fails in the younger generation. Return // generation if it fails in the younger generation. Return
// false, otherwise. // false, otherwise.
...@@ -260,6 +263,8 @@ friend class TestGenCollectorPolicy; ...@@ -260,6 +263,8 @@ friend class TestGenCollectorPolicy;
public: public:
GenCollectorPolicy(); GenCollectorPolicy();
GenGCPhaseTimes* phase_times() const { return _phase_times; }
// Accessors // Accessors
size_t min_gen0_size() { return _min_gen0_size; } size_t min_gen0_size() { return _min_gen0_size; }
size_t initial_gen0_size() { return _initial_gen0_size; } size_t initial_gen0_size() { return _initial_gen0_size; }
......
/* /*
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -625,6 +625,8 @@ void DefNewGeneration::collect(bool full, ...@@ -625,6 +625,8 @@ void DefNewGeneration::collect(bool full,
assert(gch->no_allocs_since_save_marks(0), assert(gch->no_allocs_since_save_marks(0),
"save marks have not been newly set."); "save marks have not been newly set.");
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
phase_times->note_gc_start(1);
gch->gen_process_roots(_level, gch->gen_process_roots(_level,
true, // Process younger gens, if any, true, // Process younger gens, if any,
// as strong roots. // as strong roots.
...@@ -633,7 +635,9 @@ void DefNewGeneration::collect(bool full, ...@@ -633,7 +635,9 @@ void DefNewGeneration::collect(bool full,
GenCollectedHeap::StrongAndWeakRoots, GenCollectedHeap::StrongAndWeakRoots,
&fsc_with_no_gc_barrier, &fsc_with_no_gc_barrier,
&fsc_with_gc_barrier, &fsc_with_gc_barrier,
&cld_scan_closure); &cld_scan_closure,
phase_times,
0);
// "evacuate followers". // "evacuate followers".
evacuate_followers.do_void(); evacuate_followers.do_void();
......
/* /*
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -619,7 +619,9 @@ void GenCollectedHeap::process_roots(bool activate_scope, ...@@ -619,7 +619,9 @@ void GenCollectedHeap::process_roots(bool activate_scope,
OopClosure* weak_roots, OopClosure* weak_roots,
CLDClosure* strong_cld_closure, CLDClosure* strong_cld_closure,
CLDClosure* weak_cld_closure, CLDClosure* weak_cld_closure,
CodeBlobToOopClosure* code_roots) { CodeBlobToOopClosure* code_roots,
GenGCPhaseTimes* phase_times,
uint worker_id) {
StrongRootsScope srs(this, activate_scope); StrongRootsScope srs(this, activate_scope);
// General roots. // General roots.
...@@ -630,71 +632,117 @@ void GenCollectedHeap::process_roots(bool activate_scope, ...@@ -630,71 +632,117 @@ void GenCollectedHeap::process_roots(bool activate_scope,
// could be trying to change the termination condition while the task // could be trying to change the termination condition while the task
// is executing in another GC worker. // is executing in another GC worker.
if (!_process_strong_tasks->is_task_claimed(GCH_PS_ClassLoaderDataGraph_oops_do)) { //CLDGRoots
ClassLoaderDataGraph::roots_cld_do(strong_cld_closure, weak_cld_closure); {
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::CLDGRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_ClassLoaderDataGraph_oops_do)) {
ClassLoaderDataGraph::roots_cld_do(strong_cld_closure, weak_cld_closure);
}
} }
// Some CLDs contained in the thread frames should be considered strong. // Some CLDs contained in the thread frames should be considered strong.
// Don't process them if they will be processed during the ClassLoaderDataGraph phase. // Don't process them if they will be processed during the ClassLoaderDataGraph phase.
CLDClosure* roots_from_clds_p = (strong_cld_closure != weak_cld_closure) ? strong_cld_closure : NULL; CLDClosure* roots_from_clds_p = (strong_cld_closure != weak_cld_closure) ? strong_cld_closure : NULL;
// Only process code roots from thread stacks if we aren't visiting the entire CodeCache anyway // Only process code roots from thread stacks if we aren't visiting the entire CodeCache anyway
CodeBlobToOopClosure* roots_from_code_p = (so & SO_AllCodeCache) ? NULL : code_roots; CodeBlobToOopClosure* roots_from_code_p = (so & SO_AllCodeCache) ? NULL : code_roots;
// ThreadRoots
{
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::ThreadRoots, worker_id);
Threads::possibly_parallel_oops_do(strong_roots, roots_from_clds_p, roots_from_code_p);
}
Threads::possibly_parallel_oops_do(strong_roots, roots_from_clds_p, roots_from_code_p); //UniverseRoots
{
if (!_process_strong_tasks->is_task_claimed(GCH_PS_Universe_oops_do)) { GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::UniverseRoots, worker_id);
Universe::oops_do(strong_roots); if (!_process_strong_tasks->is_task_claimed(GCH_PS_Universe_oops_do)) {
Universe::oops_do(strong_roots);
}
} }
// Global (strong) JNI handles // Global (strong) JNI handles
if (!_process_strong_tasks->is_task_claimed(GCH_PS_JNIHandles_oops_do)) { {
JNIHandles::oops_do(strong_roots); GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::JNIRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_JNIHandles_oops_do)) {
JNIHandles::oops_do(strong_roots);
}
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_ObjectSynchronizer_oops_do)) { // ObjectSynchronizerRoots
ObjectSynchronizer::oops_do(strong_roots); {
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::ObjectSynchronizerRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_ObjectSynchronizer_oops_do)) {
ObjectSynchronizer::oops_do(strong_roots);
}
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_FlatProfiler_oops_do)) {
FlatProfiler::oops_do(strong_roots); // FlatProfilerRoots
{
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::FlatProfilerRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_FlatProfiler_oops_do)) {
FlatProfiler::oops_do(strong_roots);
}
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_Management_oops_do)) {
Management::oops_do(strong_roots); // ManagementRoots
{
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::ManagementRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_Management_oops_do)) {
Management::oops_do(strong_roots);
}
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_jvmti_oops_do)) {
JvmtiExport::oops_do(strong_roots); // JVMTIRoots
{
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::JVMTIRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_jvmti_oops_do)) {
JvmtiExport::oops_do(strong_roots);
}
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_SystemDictionary_oops_do)) { // SystemDictionaryRoots
SystemDictionary::roots_oops_do(strong_roots, weak_roots); {
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::SystemDictionaryRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_SystemDictionary_oops_do)) {
SystemDictionary::roots_oops_do(strong_roots, weak_roots);
}
} }
// All threads execute the following. A specific chunk of buckets // All threads execute the following. A specific chunk of buckets
// from the StringTable are the individual tasks. // from the StringTable are the individual tasks.
if (weak_roots != NULL) { // StringTableRoots
if (CollectedHeap::use_parallel_gc_threads()) { {
StringTable::possibly_parallel_oops_do(weak_roots); GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::StringTableRoots, worker_id);
} else { if (weak_roots != NULL) {
StringTable::oops_do(weak_roots); if (CollectedHeap::use_parallel_gc_threads()) {
StringTable::possibly_parallel_oops_do(weak_roots);
} else {
StringTable::oops_do(weak_roots);
}
} }
} }
if (!_process_strong_tasks->is_task_claimed(GCH_PS_CodeCache_oops_do)) { // CodeCacheRoots
if (so & SO_ScavengeCodeCache) { {
assert(code_roots != NULL, "must supply closure for code cache"); GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::CodeCacheRoots, worker_id);
if (!_process_strong_tasks->is_task_claimed(GCH_PS_CodeCache_oops_do)) {
if (so & SO_ScavengeCodeCache) {
assert(code_roots != NULL, "must supply closure for code cache");
// We only visit parts of the CodeCache when scavenging. // We only visit parts of the CodeCache when scavenging.
CodeCache::scavenge_root_nmethods_do(code_roots); CodeCache::scavenge_root_nmethods_do(code_roots);
} }
if (so & SO_AllCodeCache) { if (so & SO_AllCodeCache) {
assert(code_roots != NULL, "must supply closure for code cache"); assert(code_roots != NULL, "must supply closure for code cache");
// CMSCollector uses this to do intermediate-strength collections. // CMSCollector uses this to do intermediate-strength collections.
// We scan the entire code cache, since CodeCache::do_unloading is not called. // We scan the entire code cache, since CodeCache::do_unloading is not called.
CodeCache::blobs_do(code_roots); CodeCache::blobs_do(code_roots);
}
// Verify that the code cache contents are not subject to
// movement by a scavenging collection.
DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, !CodeBlobToOopClosure::FixRelocations));
DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable));
} }
// Verify that the code cache contents are not subject to
// movement by a scavenging collection.
DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, !CodeBlobToOopClosure::FixRelocations));
DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable));
} }
} }
...@@ -706,9 +754,12 @@ void GenCollectedHeap::gen_process_roots(int level, ...@@ -706,9 +754,12 @@ void GenCollectedHeap::gen_process_roots(int level,
bool only_strong_roots, bool only_strong_roots,
OopsInGenClosure* not_older_gens, OopsInGenClosure* not_older_gens,
OopsInGenClosure* older_gens, OopsInGenClosure* older_gens,
CLDClosure* cld_closure) { CLDClosure* cld_closure,
GenGCPhaseTimes* phase_times,
uint worker_id) {
const bool is_adjust_phase = !only_strong_roots && !younger_gens_as_roots; const bool is_adjust_phase = !only_strong_roots && !younger_gens_as_roots;
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::RootProcess, worker_id);
bool is_moving_collection = false; bool is_moving_collection = false;
if (level == 0 || is_adjust_phase) { if (level == 0 || is_adjust_phase) {
// young collections are always moving // young collections are always moving
...@@ -722,7 +773,7 @@ void GenCollectedHeap::gen_process_roots(int level, ...@@ -722,7 +773,7 @@ void GenCollectedHeap::gen_process_roots(int level,
process_roots(activate_scope, so, process_roots(activate_scope, so,
not_older_gens, weak_roots, not_older_gens, weak_roots,
cld_closure, weak_cld_closure, cld_closure, weak_cld_closure,
&mark_code_closure); &mark_code_closure, phase_times, worker_id);
if (younger_gens_as_roots) { if (younger_gens_as_roots) {
if (!_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) { if (!_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) {
...@@ -733,14 +784,17 @@ void GenCollectedHeap::gen_process_roots(int level, ...@@ -733,14 +784,17 @@ void GenCollectedHeap::gen_process_roots(int level,
not_older_gens->reset_generation(); not_older_gens->reset_generation();
} }
} }
// When collection is parallel, all threads get to cooperate to do
// older-gen scanning.
for (int i = level+1; i < _n_gens; i++) {
older_gens->set_generation(_gens[i]);
rem_set()->younger_refs_iterate(_gens[i], older_gens);
older_gens->reset_generation();
}
{
GenGCParPhaseTimesTracker x(phase_times, GenGCPhaseTimes::OldGenScan, worker_id);
// When collection is parallel, all threads get to cooperate to do
// older-gen scanning.
for (int i = level+1; i < _n_gens; i++) {
older_gens->set_generation(_gens[i]);
rem_set()->younger_refs_iterate(_gens[i], older_gens);
older_gens->reset_generation();
}
}
_process_strong_tasks->all_tasks_completed(); _process_strong_tasks->all_tasks_completed();
} }
......
/* /*
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "memory/collectorPolicy.hpp" #include "memory/collectorPolicy.hpp"
#include "memory/generation.hpp" #include "memory/generation.hpp"
#include "memory/sharedHeap.hpp" #include "memory/sharedHeap.hpp"
#include "gc_implementation/shared/gcTraceTime.hpp"
class SubTasksDone; class SubTasksDone;
...@@ -416,7 +417,9 @@ public: ...@@ -416,7 +417,9 @@ public:
OopClosure* weak_roots, OopClosure* weak_roots,
CLDClosure* strong_cld_closure, CLDClosure* strong_cld_closure,
CLDClosure* weak_cld_closure, CLDClosure* weak_cld_closure,
CodeBlobToOopClosure* code_roots); CodeBlobToOopClosure* code_roots,
GenGCPhaseTimes* phase_times,
uint worker_id);
void gen_process_roots(int level, void gen_process_roots(int level,
bool younger_gens_as_roots, bool younger_gens_as_roots,
...@@ -427,7 +430,9 @@ public: ...@@ -427,7 +430,9 @@ public:
OopsInGenClosure* older_gens, OopsInGenClosure* older_gens,
CLDClosure* cld_closure, CLDClosure* cld_closure,
CLDClosure* weak_cld_closure, CLDClosure* weak_cld_closure,
CodeBlobClosure* code_closure); CodeBlobClosure* code_closure,
GenGCPhaseTimes* phase_times,
uint worker_id);
public: public:
static const bool StrongAndWeakRoots = false; static const bool StrongAndWeakRoots = false;
...@@ -440,7 +445,9 @@ public: ...@@ -440,7 +445,9 @@ public:
bool only_strong_roots, bool only_strong_roots,
OopsInGenClosure* not_older_gens, OopsInGenClosure* not_older_gens,
OopsInGenClosure* older_gens, OopsInGenClosure* older_gens,
CLDClosure* cld_closure); CLDClosure* cld_closure,
GenGCPhaseTimes* phase_times,
uint worker_id);
// Apply "root_closure" to all the weak roots of the system. // Apply "root_closure" to all the weak roots of the system.
// These include JNI weak roots, string table, // These include JNI weak roots, string table,
......
...@@ -197,7 +197,7 @@ void GenMarkSweep::mark_sweep_phase1(int level, ...@@ -197,7 +197,7 @@ void GenMarkSweep::mark_sweep_phase1(int level,
trace(" 1"); trace(" 1");
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
// Because follow_root_closure is created statically, cannot // Because follow_root_closure is created statically, cannot
// use OopsInGenClosure constructor which takes a generation, // use OopsInGenClosure constructor which takes a generation,
// as the Universe has not been created when the static constructors // as the Universe has not been created when the static constructors
...@@ -206,7 +206,8 @@ void GenMarkSweep::mark_sweep_phase1(int level, ...@@ -206,7 +206,8 @@ void GenMarkSweep::mark_sweep_phase1(int level,
// Need new claim bits before marking starts. // Need new claim bits before marking starts.
ClassLoaderDataGraph::clear_claimed_marks(); ClassLoaderDataGraph::clear_claimed_marks();
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(level, gch->gen_process_roots(level,
false, // Younger gens are not roots. false, // Younger gens are not roots.
true, // activate StrongRootsScope true, // activate StrongRootsScope
...@@ -214,8 +215,10 @@ void GenMarkSweep::mark_sweep_phase1(int level, ...@@ -214,8 +215,10 @@ void GenMarkSweep::mark_sweep_phase1(int level,
ClassUnloading, ClassUnloading,
&follow_root_closure, &follow_root_closure,
&follow_root_closure, &follow_root_closure,
&follow_cld_closure); &follow_cld_closure,
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
// Process reference objects found during marking // Process reference objects found during marking
{ {
ref_processor()->setup_policy(clear_all_softrefs); ref_processor()->setup_policy(clear_all_softrefs);
...@@ -225,6 +228,11 @@ void GenMarkSweep::mark_sweep_phase1(int level, ...@@ -225,6 +228,11 @@ void GenMarkSweep::mark_sweep_phase1(int level,
gc_tracer()->report_gc_reference_stats(stats); gc_tracer()->report_gc_reference_stats(stats);
} }
phase_times->note_gc_end();
if (PrintGCRootsTraceTime && PrintGCDetails) {
phase_times->log_gc_details();
}
// This is the point where the entire marking should have completed. // This is the point where the entire marking should have completed.
assert(_marking_stack.is_empty(), "Marking should have completed"); assert(_marking_stack.is_empty(), "Marking should have completed");
...@@ -246,7 +254,6 @@ void GenMarkSweep::mark_sweep_phase1(int level, ...@@ -246,7 +254,6 @@ void GenMarkSweep::mark_sweep_phase1(int level,
gc_tracer()->report_object_count_after_gc(&is_alive); gc_tracer()->report_object_count_after_gc(&is_alive);
} }
void GenMarkSweep::mark_sweep_phase2() { void GenMarkSweep::mark_sweep_phase2() {
// Now all live objects are marked, compute the new object addresses. // Now all live objects are marked, compute the new object addresses.
...@@ -279,6 +286,8 @@ public: ...@@ -279,6 +286,8 @@ public:
void GenMarkSweep::mark_sweep_phase3(int level) { void GenMarkSweep::mark_sweep_phase3(int level) {
GenCollectedHeap* gch = GenCollectedHeap::heap(); GenCollectedHeap* gch = GenCollectedHeap::heap();
GenGCPhaseTimes* phase_times = gch->gen_policy()->phase_times();
// Adjust the pointers to reflect the new locations // Adjust the pointers to reflect the new locations
GCTraceTime tm("phase 3", PrintGC && Verbose, true, _gc_timer, _gc_tracer->gc_id()); GCTraceTime tm("phase 3", PrintGC && Verbose, true, _gc_timer, _gc_tracer->gc_id());
trace("3"); trace("3");
...@@ -292,6 +301,9 @@ void GenMarkSweep::mark_sweep_phase3(int level) { ...@@ -292,6 +301,9 @@ void GenMarkSweep::mark_sweep_phase3(int level) {
// are run. // are run.
adjust_pointer_closure.set_orig_generation(gch->get_gen(level)); adjust_pointer_closure.set_orig_generation(gch->get_gen(level));
phase_times->note_gc_start(1);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerStart, 0, os::elapsedTime());
gch->gen_process_roots(level, gch->gen_process_roots(level,
false, // Younger gens are not roots. false, // Younger gens are not roots.
true, // activate StrongRootsScope true, // activate StrongRootsScope
...@@ -299,7 +311,15 @@ void GenMarkSweep::mark_sweep_phase3(int level) { ...@@ -299,7 +311,15 @@ void GenMarkSweep::mark_sweep_phase3(int level) {
GenCollectedHeap::StrongAndWeakRoots, GenCollectedHeap::StrongAndWeakRoots,
&adjust_pointer_closure, &adjust_pointer_closure,
&adjust_pointer_closure, &adjust_pointer_closure,
&adjust_cld_closure); &adjust_cld_closure,
phase_times,
0);
phase_times->record_time_secs(GenGCPhaseTimes::GCWorkerEnd, 0, os::elapsedTime());
phase_times->note_gc_end();
if (PrintGCRootsTraceTime && PrintGCDetails) {
phase_times->log_gc_details();
}
gch->gen_process_weak_roots(&adjust_pointer_closure); gch->gen_process_weak_roots(&adjust_pointer_closure);
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
\ \
manageable(bool, PrintYoungGenHistoAfterParNewGC, false, \ manageable(bool, PrintYoungGenHistoAfterParNewGC, false, \
"print the young generation class histogram after parNew GC") \ "print the young generation class histogram after parNew GC") \
\
manageable(bool, PrintGCRootsTraceTime, false, \
"Print GC Trace Time") \
//add new AJVM specific flags here //add new AJVM specific flags here
......
/*
* Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* @test TestPrintGCDetailTraceTime
* @summary test print gc detail time
* @library /testlibrary
* @build TestPrintGCRootsTraceTime
* @run main/othervm TestPrintGCRootsTraceTime
*/
import java.util.ArrayList;
import com.oracle.java.testlibrary.*;
public class TestPrintGCRootsTraceTime {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = null;
OutputAnalyzer output = null;
// test with the flag on
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmx200m",
"-XX:+UseParNewGC",
"-XX:+UseConcMarkSweepGC",
"-XX:+PrintGCDetails",
"-verbose:gc",
"-XX:+PrintGCRootsTraceTime",
Foo.class.getName()
);
output = new OutputAnalyzer(pb.start());
output.shouldContain("Root Processing");
String [] strOut = output.getStdout().split("\n");
for (int i = 0; i < 11; i++) {
System.out.println(strOut[i]);
}
output.shouldHaveExitValue(0);
// test with the flag off
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseParNewGC",
"-XX:+UseConcMarkSweepGC",
"-XX:+PrintGCDetails",
"-verbose:gc",
"-XX:-PrintGCRootsTraceTime",
Foo.class.getName()
);
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test serial gc
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseSerialGC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test g1 gc
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseG1GC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test ps gc
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseParallelGC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test ps young gc
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseParallelGC",
"-XX:-UseParallelOldGC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test cms without ParNew
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseConcMarkSweepGC",
"-XX:-UseParNewGC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Root Processing");
output.shouldHaveExitValue(0);
// test cms
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseConcMarkSweepGC",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldContain("Root Processing");
output.shouldHaveExitValue(0);
// test cms with parallel gc threads = 1
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+UseConcMarkSweepGC",
"-XX:+PrintGCDetails",
"-XX:ParallelGCThreads=1",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
Foo.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldContain("Root Processing");
output.shouldHaveExitValue(0);
// test system gc
pb = ProcessTools.createJavaProcessBuilder( "-Xmx500m",
"-Xmn200m",
"-XX:+PrintGCDetails",
"-XX:+PrintGCRootsTraceTime",
"-verbose:gc",
TestSystemGC.class.getName());
output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
}
// class for testing gc
public static class Foo {
public static void main(String []args) {
int count = 0;
ArrayList list = new ArrayList();
for (int i = 0; ; i++) {
byte[] data = new byte[1024];
list.add(data);
if (i % 1024 == 0) {
count++;
list.clear();
if (count > 1024) {
break;
}
}
}
}
}
// class for testing system gc
public static class TestSystemGC {
public static void main(String []args) throws Exception {
System.gc();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册