7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
It introduces ergonomic decision logging in G1 for the following heuristics: heap sizing, collection set construction, concurrent cycle initiation, and partially-young GC start/end. The code has a bit of refactoring in a few places to make the decision logging possible. It also replaces alternative ad-hoc logging that we have under different parameters and switches (G1_DEBUG, G1PolicyVerbose). Reviewed-by: johnc, ysr
This commit is contained in:
parent
a9cd50a1c8
commit
24eb07061e
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2011, 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include "gc_implementation/g1/collectionSetChooser.hpp"
|
||||
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
|
||||
#include "gc_implementation/g1/g1ErgoVerbose.hpp"
|
||||
#include "memory/space.inline.hpp"
|
||||
|
||||
CSetChooserCache::CSetChooserCache() {
|
||||
@ -358,6 +359,9 @@ CollectionSetChooser::getNextMarkedRegion(double time_remaining,
|
||||
if (_cache.is_empty()) {
|
||||
assert(_curMarkedIndex == _numMarkedRegions,
|
||||
"if cache is empty, list should also be empty");
|
||||
ergo_verbose0(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("cache is empty"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -368,10 +372,23 @@ CollectionSetChooser::getNextMarkedRegion(double time_remaining,
|
||||
if (g1p->adaptive_young_list_length()) {
|
||||
if (time_remaining - predicted_time < 0.0) {
|
||||
g1h->check_if_region_is_too_expensive(predicted_time);
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("predicted old region time higher than remaining time")
|
||||
ergo_format_ms("predicted old region time")
|
||||
ergo_format_ms("remaining time"),
|
||||
predicted_time, time_remaining);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (predicted_time > 2.0 * avg_prediction) {
|
||||
double threshold = 2.0 * avg_prediction;
|
||||
if (predicted_time > threshold) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("predicted old region time higher than threshold")
|
||||
ergo_format_ms("predicted old region time")
|
||||
ergo_format_ms("threshold"),
|
||||
predicted_time, threshold);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "gc_implementation/g1/concurrentMarkThread.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
|
||||
#include "gc_implementation/g1/g1ErgoVerbose.hpp"
|
||||
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
|
||||
#include "gc_implementation/g1/g1RemSet.hpp"
|
||||
#include "gc_implementation/g1/heapRegionRemSet.hpp"
|
||||
@ -1727,18 +1728,21 @@ void ConcurrentMark::cleanup() {
|
||||
|
||||
size_t known_garbage_bytes =
|
||||
g1_par_count_task.used_bytes() - g1_par_count_task.live_bytes();
|
||||
#if 0
|
||||
gclog_or_tty->print_cr("used %1.2lf, live %1.2lf, garbage %1.2lf",
|
||||
(double) g1_par_count_task.used_bytes() / (double) (1024 * 1024),
|
||||
(double) g1_par_count_task.live_bytes() / (double) (1024 * 1024),
|
||||
(double) known_garbage_bytes / (double) (1024 * 1024));
|
||||
#endif // 0
|
||||
g1p->set_known_garbage_bytes(known_garbage_bytes);
|
||||
|
||||
size_t start_used_bytes = g1h->used();
|
||||
_at_least_one_mark_complete = true;
|
||||
g1h->set_marking_complete();
|
||||
|
||||
ergo_verbose4(ErgoConcCycles,
|
||||
"finish cleanup",
|
||||
ergo_format_byte("occupancy")
|
||||
ergo_format_byte("capacity")
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
start_used_bytes, g1h->capacity(),
|
||||
known_garbage_bytes,
|
||||
((double) known_garbage_bytes / (double) g1h->capacity()) * 100.0);
|
||||
|
||||
double count_end = os::elapsedTime();
|
||||
double this_final_counting_time = (count_end - start);
|
||||
if (G1PrintParCleanupStats) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "gc_implementation/g1/g1AllocRegion.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
|
||||
#include "gc_implementation/g1/g1ErgoVerbose.hpp"
|
||||
#include "gc_implementation/g1/g1MarkSweep.hpp"
|
||||
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
|
||||
#include "gc_implementation/g1/g1RemSet.inline.hpp"
|
||||
@ -577,6 +578,11 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool do_expand) {
|
||||
res = new_region_try_secondary_free_list();
|
||||
}
|
||||
if (res == NULL && do_expand) {
|
||||
ergo_verbose1(ErgoHeapSizing,
|
||||
"attempt heap expansion",
|
||||
ergo_format_reason("region allocation request failed")
|
||||
ergo_format_byte("allocation request"),
|
||||
word_size * HeapWordSize);
|
||||
if (expand(word_size * HeapWordSize)) {
|
||||
// Even though the heap was expanded, it might not have reached
|
||||
// the desired size. So, we cannot assume that the allocation
|
||||
@ -790,6 +796,11 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) {
|
||||
// room available.
|
||||
assert(num_regions > fs, "earlier allocation should have succeeded");
|
||||
|
||||
ergo_verbose1(ErgoHeapSizing,
|
||||
"attempt heap expansion",
|
||||
ergo_format_reason("humongous allocation request failed")
|
||||
ergo_format_byte("allocation request"),
|
||||
word_size * HeapWordSize);
|
||||
if (expand((num_regions - fs) * HeapRegion::GrainBytes)) {
|
||||
// Even though the heap was expanded, it might not have
|
||||
// reached the desired size. So, we cannot assume that the
|
||||
@ -906,6 +917,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
|
||||
|
||||
if (GC_locker::is_active_and_needs_gc()) {
|
||||
if (g1_policy()->can_expand_young_list()) {
|
||||
// No need for an ergo verbose message here,
|
||||
// can_expand_young_list() does this when it returns true.
|
||||
result = _mutator_alloc_region.attempt_allocation_force(word_size,
|
||||
false /* bot_updates */);
|
||||
if (result != NULL) {
|
||||
@ -1477,63 +1490,34 @@ resize_if_necessary_after_full_collection(size_t word_size) {
|
||||
// we'll try to make the capacity smaller than it, not greater).
|
||||
maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size);
|
||||
|
||||
if (PrintGC && Verbose) {
|
||||
const double free_percentage =
|
||||
(double) free_after_gc / (double) capacity_after_gc;
|
||||
gclog_or_tty->print_cr("Computing new size after full GC ");
|
||||
gclog_or_tty->print_cr(" "
|
||||
" minimum_free_percentage: %6.2f",
|
||||
minimum_free_percentage);
|
||||
gclog_or_tty->print_cr(" "
|
||||
" maximum_free_percentage: %6.2f",
|
||||
maximum_free_percentage);
|
||||
gclog_or_tty->print_cr(" "
|
||||
" capacity: %6.1fK"
|
||||
" minimum_desired_capacity: %6.1fK"
|
||||
" maximum_desired_capacity: %6.1fK",
|
||||
(double) capacity_after_gc / (double) K,
|
||||
(double) minimum_desired_capacity / (double) K,
|
||||
(double) maximum_desired_capacity / (double) K);
|
||||
gclog_or_tty->print_cr(" "
|
||||
" free_after_gc: %6.1fK"
|
||||
" used_after_gc: %6.1fK",
|
||||
(double) free_after_gc / (double) K,
|
||||
(double) used_after_gc / (double) K);
|
||||
gclog_or_tty->print_cr(" "
|
||||
" free_percentage: %6.2f",
|
||||
free_percentage);
|
||||
}
|
||||
if (capacity_after_gc < minimum_desired_capacity) {
|
||||
// Don't expand unless it's significant
|
||||
size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
|
||||
if (expand(expand_bytes)) {
|
||||
if (PrintGC && Verbose) {
|
||||
gclog_or_tty->print_cr(" "
|
||||
" expanding:"
|
||||
" max_heap_size: %6.1fK"
|
||||
" minimum_desired_capacity: %6.1fK"
|
||||
" expand_bytes: %6.1fK",
|
||||
(double) max_heap_size / (double) K,
|
||||
(double) minimum_desired_capacity / (double) K,
|
||||
(double) expand_bytes / (double) K);
|
||||
}
|
||||
}
|
||||
ergo_verbose4(ErgoHeapSizing,
|
||||
"attempt heap expansion",
|
||||
ergo_format_reason("capacity lower than "
|
||||
"min desired capacity after Full GC")
|
||||
ergo_format_byte("capacity")
|
||||
ergo_format_byte("occupancy")
|
||||
ergo_format_byte_perc("min desired capacity"),
|
||||
capacity_after_gc, used_after_gc,
|
||||
minimum_desired_capacity, (double) MinHeapFreeRatio);
|
||||
expand(expand_bytes);
|
||||
|
||||
// No expansion, now see if we want to shrink
|
||||
} else if (capacity_after_gc > maximum_desired_capacity) {
|
||||
// Capacity too large, compute shrinking size
|
||||
size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity;
|
||||
ergo_verbose4(ErgoHeapSizing,
|
||||
"attempt heap shrinking",
|
||||
ergo_format_reason("capacity higher than "
|
||||
"max desired capacity after Full GC")
|
||||
ergo_format_byte("capacity")
|
||||
ergo_format_byte("occupancy")
|
||||
ergo_format_byte_perc("max desired capacity"),
|
||||
capacity_after_gc, used_after_gc,
|
||||
maximum_desired_capacity, (double) MaxHeapFreeRatio);
|
||||
shrink(shrink_bytes);
|
||||
if (PrintGC && Verbose) {
|
||||
gclog_or_tty->print_cr(" "
|
||||
" shrinking:"
|
||||
" min_heap_size: %6.1fK"
|
||||
" maximum_desired_capacity: %6.1fK"
|
||||
" shrink_bytes: %6.1fK",
|
||||
(double) min_heap_size / (double) K,
|
||||
(double) maximum_desired_capacity / (double) K,
|
||||
(double) shrink_bytes / (double) K);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1619,6 +1603,11 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) {
|
||||
verify_region_sets_optional();
|
||||
|
||||
size_t expand_bytes = MAX2(word_size * HeapWordSize, MinHeapDeltaBytes);
|
||||
ergo_verbose1(ErgoHeapSizing,
|
||||
"attempt heap expansion",
|
||||
ergo_format_reason("allocation request failed")
|
||||
ergo_format_byte("allocation request"),
|
||||
word_size * HeapWordSize);
|
||||
if (expand(expand_bytes)) {
|
||||
_hrs.verify_optional();
|
||||
verify_region_sets_optional();
|
||||
@ -1646,11 +1635,11 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes);
|
||||
aligned_expand_bytes = align_size_up(aligned_expand_bytes,
|
||||
HeapRegion::GrainBytes);
|
||||
|
||||
if (Verbose && PrintGC) {
|
||||
gclog_or_tty->print("Expanding garbage-first heap from %ldK by %ldK",
|
||||
old_mem_size/K, aligned_expand_bytes/K);
|
||||
}
|
||||
ergo_verbose2(ErgoHeapSizing,
|
||||
"expand the heap",
|
||||
ergo_format_byte("requested expansion amount")
|
||||
ergo_format_byte("attempted expansion amount"),
|
||||
expand_bytes, aligned_expand_bytes);
|
||||
|
||||
// First commit the memory.
|
||||
HeapWord* old_end = (HeapWord*) _g1_storage.high();
|
||||
@ -1694,6 +1683,9 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
assert(curr == mr.end(), "post-condition");
|
||||
}
|
||||
} else {
|
||||
ergo_verbose0(ErgoHeapSizing,
|
||||
"did not expand the heap",
|
||||
ergo_format_reason("heap expansion operation failed"));
|
||||
// The expansion of the virtual storage space was unsuccessful.
|
||||
// Let's see if it was because we ran out of swap.
|
||||
if (G1ExitOnExpansionFailure &&
|
||||
@ -1702,13 +1694,6 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
vm_exit_out_of_memory(aligned_expand_bytes, "G1 heap expansion");
|
||||
}
|
||||
}
|
||||
|
||||
if (Verbose && PrintGC) {
|
||||
size_t new_mem_size = _g1_storage.committed_size();
|
||||
gclog_or_tty->print_cr("...%s, expanded to %ldK",
|
||||
(successful ? "Successful" : "Failed"),
|
||||
new_mem_size/K);
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
@ -1722,6 +1707,13 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
|
||||
MemRegion mr = _hrs.shrink_by(aligned_shrink_bytes, &num_regions_deleted);
|
||||
HeapWord* old_end = (HeapWord*) _g1_storage.high();
|
||||
assert(mr.end() == old_end, "post-condition");
|
||||
|
||||
ergo_verbose3(ErgoHeapSizing,
|
||||
"shrink the heap",
|
||||
ergo_format_byte("requested shrinking amount")
|
||||
ergo_format_byte("aligned shrinking amount")
|
||||
ergo_format_byte("attempted shrinking amount"),
|
||||
shrink_bytes, aligned_shrink_bytes, mr.byte_size());
|
||||
if (mr.byte_size() > 0) {
|
||||
if (_hr_printer.is_active()) {
|
||||
HeapWord* curr = mr.end();
|
||||
@ -1740,13 +1732,10 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
|
||||
_expansion_regions += num_regions_deleted;
|
||||
update_committed_space(old_end, new_end);
|
||||
HeapRegionRemSet::shrink_heap(n_regions());
|
||||
|
||||
if (Verbose && PrintGC) {
|
||||
size_t new_mem_size = _g1_storage.committed_size();
|
||||
gclog_or_tty->print_cr("Shrinking garbage-first heap from %ldK by %ldK to %ldK",
|
||||
old_mem_size/K, aligned_shrink_bytes/K,
|
||||
new_mem_size/K);
|
||||
}
|
||||
} else {
|
||||
ergo_verbose0(ErgoHeapSizing,
|
||||
"did not shrink the heap",
|
||||
ergo_format_reason("heap shrinking operation failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3579,6 +3568,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||
size_t expand_bytes = g1_policy()->expansion_amount();
|
||||
if (expand_bytes > 0) {
|
||||
size_t bytes_before = capacity();
|
||||
// No need for an ergo verbose message here,
|
||||
// expansion_amount() does this when it returns a value > 0.
|
||||
if (!expand(expand_bytes)) {
|
||||
// We failed to expand the heap so let's verify that
|
||||
// committed/uncommitted amount match the backing store
|
||||
@ -3732,13 +3723,6 @@ public:
|
||||
bool do_object_b(oop p) {
|
||||
// It is reachable if it is outside the collection set, or is inside
|
||||
// and forwarded.
|
||||
|
||||
#ifdef G1_DEBUG
|
||||
gclog_or_tty->print_cr("is alive "PTR_FORMAT" in CS %d forwarded %d overall %d",
|
||||
(void*) p, _g1->obj_in_cs(p), p->is_forwarded(),
|
||||
!_g1->obj_in_cs(p) || p->is_forwarded());
|
||||
#endif // G1_DEBUG
|
||||
|
||||
return !_g1->obj_in_cs(p) || p->is_forwarded();
|
||||
}
|
||||
};
|
||||
@ -3750,20 +3734,9 @@ public:
|
||||
void do_oop(narrowOop* p) { guarantee(false, "Not needed"); }
|
||||
void do_oop( oop* p) {
|
||||
oop obj = *p;
|
||||
#ifdef G1_DEBUG
|
||||
if (PrintGC && Verbose) {
|
||||
gclog_or_tty->print_cr("keep alive *"PTR_FORMAT" = "PTR_FORMAT" "PTR_FORMAT,
|
||||
p, (void*) obj, (void*) *p);
|
||||
}
|
||||
#endif // G1_DEBUG
|
||||
|
||||
if (_g1->obj_in_cs(obj)) {
|
||||
assert( obj->is_forwarded(), "invariant" );
|
||||
*p = obj->forwardee();
|
||||
#ifdef G1_DEBUG
|
||||
gclog_or_tty->print_cr(" in CSet: moved "PTR_FORMAT" -> "PTR_FORMAT,
|
||||
(void*) obj, (void*) *p);
|
||||
#endif // G1_DEBUG
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "gc_implementation/g1/concurrentMarkThread.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
|
||||
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
|
||||
#include "gc_implementation/g1/g1ErgoVerbose.hpp"
|
||||
#include "gc_implementation/g1/heapRegionRemSet.hpp"
|
||||
#include "gc_implementation/shared/gcPolicyCounters.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
@ -271,15 +272,26 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
_recorded_survivor_tail(NULL),
|
||||
_survivors_age_table(true),
|
||||
|
||||
_gc_overhead_perc(0.0)
|
||||
_gc_overhead_perc(0.0) {
|
||||
|
||||
{
|
||||
// Set up the region size and associated fields. Given that the
|
||||
// policy is created before the heap, we have to set this up here,
|
||||
// so it's done as soon as possible.
|
||||
HeapRegion::setup_heap_region_size(Arguments::min_heap_size());
|
||||
HeapRegionRemSet::setup_remset_size();
|
||||
|
||||
G1ErgoVerbose::initialize();
|
||||
if (PrintAdaptiveSizePolicy) {
|
||||
// Currently, we only use a single switch for all the heuristics.
|
||||
G1ErgoVerbose::set_enabled(true);
|
||||
// Given that we don't currently have a verboseness level
|
||||
// parameter, we'll hardcode this to high. This can be easily
|
||||
// changed in the future.
|
||||
G1ErgoVerbose::set_level(ErgoHigh);
|
||||
} else {
|
||||
G1ErgoVerbose::set_enabled(false);
|
||||
}
|
||||
|
||||
// Verify PLAB sizes
|
||||
const uint region_size = HeapRegion::GrainWords;
|
||||
if (YoungPLABSize > region_size || OldPLABSize > region_size) {
|
||||
@ -959,11 +971,9 @@ void
|
||||
G1CollectorPolicy::
|
||||
record_concurrent_mark_cleanup_end_work1(size_t freed_bytes,
|
||||
size_t max_live_bytes) {
|
||||
if (_n_marks < 2) _n_marks++;
|
||||
if (G1PolicyVerbose > 0)
|
||||
gclog_or_tty->print_cr("At end of marking, max_live is " SIZE_FORMAT " MB "
|
||||
" (of " SIZE_FORMAT " MB heap).",
|
||||
max_live_bytes/M, _g1->capacity()/M);
|
||||
if (_n_marks < 2) {
|
||||
_n_marks++;
|
||||
}
|
||||
}
|
||||
|
||||
// The important thing about this is that it includes "os::elapsedTime".
|
||||
@ -977,14 +987,6 @@ void G1CollectorPolicy::record_concurrent_mark_cleanup_end_work2() {
|
||||
_mmu_tracker->add_pause(_mark_cleanup_start_sec, end_time_sec, true);
|
||||
|
||||
_num_markings++;
|
||||
|
||||
// We did a marking, so reset the "since_last_mark" variables.
|
||||
double considerConcMarkCost = 1.0;
|
||||
// If there are available processors, concurrent activity is free...
|
||||
if (Threads::number_of_non_daemon_threads() * 2 <
|
||||
os::active_processor_count()) {
|
||||
considerConcMarkCost = 0.0;
|
||||
}
|
||||
_n_pauses_at_mark_end = _n_pauses;
|
||||
_n_marks_since_last_pause++;
|
||||
}
|
||||
@ -1148,20 +1150,37 @@ void G1CollectorPolicy::record_collection_pause_end() {
|
||||
if (last_pause_included_initial_mark)
|
||||
record_concurrent_mark_init_end(0.0);
|
||||
|
||||
size_t min_used_targ =
|
||||
size_t marking_initiating_used_threshold =
|
||||
(_g1->capacity() / 100) * InitiatingHeapOccupancyPercent;
|
||||
|
||||
|
||||
if (!_g1->mark_in_progress() && !_last_full_young_gc) {
|
||||
assert(!last_pause_included_initial_mark, "invariant");
|
||||
if (cur_used_bytes > min_used_targ &&
|
||||
cur_used_bytes > _prev_collection_pause_used_at_end_bytes) {
|
||||
if (cur_used_bytes > marking_initiating_used_threshold) {
|
||||
if (cur_used_bytes > _prev_collection_pause_used_at_end_bytes) {
|
||||
assert(!during_initial_mark_pause(), "we should not see this here");
|
||||
|
||||
ergo_verbose3(ErgoConcCycles,
|
||||
"request concurrent cycle initiation",
|
||||
ergo_format_reason("occupancy higher than threshold")
|
||||
ergo_format_byte("occupancy")
|
||||
ergo_format_byte_perc("threshold"),
|
||||
cur_used_bytes,
|
||||
marking_initiating_used_threshold,
|
||||
(double) InitiatingHeapOccupancyPercent);
|
||||
|
||||
// Note: this might have already been set, if during the last
|
||||
// pause we decided to start a cycle but at the beginning of
|
||||
// this pause we decided to postpone it. That's OK.
|
||||
set_initiate_conc_mark_if_possible();
|
||||
} else {
|
||||
ergo_verbose2(ErgoConcCycles,
|
||||
"do not request concurrent cycle initiation",
|
||||
ergo_format_reason("occupancy lower than previous occupancy")
|
||||
ergo_format_byte("occupancy")
|
||||
ergo_format_byte("previous occupancy"),
|
||||
cur_used_bytes,
|
||||
_prev_collection_pause_used_at_end_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1437,16 +1456,45 @@ void G1CollectorPolicy::record_collection_pause_end() {
|
||||
}
|
||||
|
||||
if (_last_full_young_gc) {
|
||||
ergo_verbose2(ErgoPartiallyYoungGCs,
|
||||
"start partially-young GCs",
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
set_full_young_gcs(false);
|
||||
_last_full_young_gc = false;
|
||||
}
|
||||
|
||||
if ( !_last_young_gc_full ) {
|
||||
if ( _should_revert_to_full_young_gcs ||
|
||||
_known_garbage_ratio < 0.05 ||
|
||||
(adaptive_young_list_length() &&
|
||||
(get_gc_eff_factor() * cur_efficiency < predict_young_gc_eff())) ) {
|
||||
set_full_young_gcs(true);
|
||||
if (_should_revert_to_full_young_gcs) {
|
||||
ergo_verbose2(ErgoPartiallyYoungGCs,
|
||||
"end partially-young GCs",
|
||||
ergo_format_reason("partially-young GCs end requested")
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
set_full_young_gcs(true);
|
||||
} else if (_known_garbage_ratio < 0.05) {
|
||||
ergo_verbose3(ErgoPartiallyYoungGCs,
|
||||
"end partially-young GCs",
|
||||
ergo_format_reason("known garbage percent lower than threshold")
|
||||
ergo_format_byte_perc("known garbage")
|
||||
ergo_format_perc("threshold"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0,
|
||||
0.05 * 100.0);
|
||||
set_full_young_gcs(true);
|
||||
} else if (adaptive_young_list_length() &&
|
||||
(get_gc_eff_factor() * cur_efficiency < predict_young_gc_eff())) {
|
||||
ergo_verbose5(ErgoPartiallyYoungGCs,
|
||||
"end partially-young GCs",
|
||||
ergo_format_reason("current GC efficiency lower than "
|
||||
"predicted fully-young GC efficiency")
|
||||
ergo_format_double("GC efficiency factor")
|
||||
ergo_format_double("current GC efficiency")
|
||||
ergo_format_double("predicted fully-young GC efficiency")
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
get_gc_eff_factor(), cur_efficiency,
|
||||
predict_young_gc_eff(),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
set_full_young_gcs(true);
|
||||
}
|
||||
}
|
||||
_should_revert_to_full_young_gcs = false;
|
||||
@ -1877,6 +1925,12 @@ void G1CollectorPolicy::check_if_region_is_too_expensive(double
|
||||
// I don't think we need to do this when in young GC mode since
|
||||
// marking will be initiated next time we hit the soft limit anyway...
|
||||
if (predicted_time_ms > _expensive_region_limit_ms) {
|
||||
ergo_verbose2(ErgoPartiallyYoungGCs,
|
||||
"request partially-young GCs end",
|
||||
ergo_format_reason("predicted region time higher than threshold")
|
||||
ergo_format_ms("predicted region time")
|
||||
ergo_format_ms("threshold"),
|
||||
predicted_time_ms, _expensive_region_limit_ms);
|
||||
// no point in doing another partial one
|
||||
_should_revert_to_full_young_gcs = true;
|
||||
}
|
||||
@ -1986,7 +2040,9 @@ G1CollectorPolicy::conservative_avg_survival_fraction_work(double avg,
|
||||
}
|
||||
|
||||
size_t G1CollectorPolicy::expansion_amount() {
|
||||
if ((recent_avg_pause_time_ratio() * 100.0) > _gc_overhead_perc) {
|
||||
double recent_gc_overhead = recent_avg_pause_time_ratio() * 100.0;
|
||||
double threshold = _gc_overhead_perc;
|
||||
if (recent_gc_overhead > threshold) {
|
||||
// We will double the existing space, or take
|
||||
// G1ExpandByPercentOfAvailable % of the available expansion
|
||||
// space, whichever is smaller, bounded below by a minimum
|
||||
@ -2001,20 +2057,19 @@ size_t G1CollectorPolicy::expansion_amount() {
|
||||
expand_bytes = MIN2(expand_bytes_via_pct, committed_bytes);
|
||||
expand_bytes = MAX2(expand_bytes, min_expand_bytes);
|
||||
expand_bytes = MIN2(expand_bytes, uncommitted_bytes);
|
||||
if (G1PolicyVerbose > 1) {
|
||||
gclog_or_tty->print("Decided to expand: ratio = %5.2f, "
|
||||
"committed = %d%s, uncommited = %d%s, via pct = %d%s.\n"
|
||||
" Answer = %d.\n",
|
||||
recent_avg_pause_time_ratio(),
|
||||
byte_size_in_proper_unit(committed_bytes),
|
||||
proper_unit_for_byte_size(committed_bytes),
|
||||
byte_size_in_proper_unit(uncommitted_bytes),
|
||||
proper_unit_for_byte_size(uncommitted_bytes),
|
||||
byte_size_in_proper_unit(expand_bytes_via_pct),
|
||||
proper_unit_for_byte_size(expand_bytes_via_pct),
|
||||
byte_size_in_proper_unit(expand_bytes),
|
||||
proper_unit_for_byte_size(expand_bytes));
|
||||
}
|
||||
|
||||
ergo_verbose5(ErgoHeapSizing,
|
||||
"attempt heap expansion",
|
||||
ergo_format_reason("recent GC overhead higher than "
|
||||
"threshold after GC")
|
||||
ergo_format_perc("recent GC overhead")
|
||||
ergo_format_perc("threshold")
|
||||
ergo_format_byte("uncommitted")
|
||||
ergo_format_byte_perc("calculated expansion amount"),
|
||||
recent_gc_overhead, threshold,
|
||||
uncommitted_bytes,
|
||||
expand_bytes_via_pct, (double) G1ExpandByPercentOfAvailable);
|
||||
|
||||
return expand_bytes;
|
||||
} else {
|
||||
return 0;
|
||||
@ -2237,8 +2292,7 @@ void G1CollectorPolicy::print_yg_surv_rate_info() const {
|
||||
#endif // PRODUCT
|
||||
}
|
||||
|
||||
void
|
||||
G1CollectorPolicy::update_region_num(bool young) {
|
||||
void G1CollectorPolicy::update_region_num(bool young) {
|
||||
if (young) {
|
||||
++_region_num_young;
|
||||
} else {
|
||||
@ -2315,13 +2369,23 @@ bool G1CollectorPolicy_BestRegionsFirst::assertMarkedBytesDataOK() {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
G1CollectorPolicy::force_initial_mark_if_outside_cycle() {
|
||||
bool G1CollectorPolicy::force_initial_mark_if_outside_cycle(
|
||||
GCCause::Cause gc_cause) {
|
||||
bool during_cycle = _g1->concurrent_mark()->cmThread()->during_cycle();
|
||||
if (!during_cycle) {
|
||||
ergo_verbose1(ErgoConcCycles,
|
||||
"request concurrent cycle initiation",
|
||||
ergo_format_reason("requested by GC cause")
|
||||
ergo_format_str("GC cause"),
|
||||
GCCause::to_string(gc_cause));
|
||||
set_initiate_conc_mark_if_possible();
|
||||
return true;
|
||||
} else {
|
||||
ergo_verbose1(ErgoConcCycles,
|
||||
"do not request concurrent cycle initiation",
|
||||
ergo_format_reason("concurrent cycle already in progress")
|
||||
ergo_format_str("GC cause"),
|
||||
GCCause::to_string(gc_cause));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2353,6 +2417,10 @@ G1CollectorPolicy::decide_on_conc_mark_initiation() {
|
||||
// And we can now clear initiate_conc_mark_if_possible() as
|
||||
// we've already acted on it.
|
||||
clear_initiate_conc_mark_if_possible();
|
||||
|
||||
ergo_verbose0(ErgoConcCycles,
|
||||
"initiate concurrent cycle",
|
||||
ergo_format_reason("concurrent cycle initiation requested"));
|
||||
} else {
|
||||
// The concurrent marking thread is still finishing up the
|
||||
// previous cycle. If we start one right now the two cycles
|
||||
@ -2366,6 +2434,9 @@ G1CollectorPolicy::decide_on_conc_mark_initiation() {
|
||||
// and, if it's in a yield point, it's waiting for us to
|
||||
// finish. So, at this point we will not start a cycle and we'll
|
||||
// let the concurrent marking thread complete the last one.
|
||||
ergo_verbose0(ErgoConcCycles,
|
||||
"do not initiate concurrent cycle",
|
||||
ergo_format_reason("concurrent cycle already in progress"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2756,6 +2827,8 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
// Set this here - in case we're not doing young collections.
|
||||
double non_young_start_time_sec = os::elapsedTime();
|
||||
|
||||
YoungList* young_list = _g1->young_list();
|
||||
|
||||
start_recording_regions();
|
||||
|
||||
guarantee(target_pause_time_ms > 0.0,
|
||||
@ -2768,61 +2841,62 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
|
||||
double time_remaining_ms = target_pause_time_ms - base_time_ms;
|
||||
|
||||
ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
|
||||
"start choosing CSet",
|
||||
ergo_format_ms("predicted base time")
|
||||
ergo_format_ms("remaining time")
|
||||
ergo_format_ms("target pause time"),
|
||||
base_time_ms, time_remaining_ms, target_pause_time_ms);
|
||||
|
||||
// the 10% and 50% values are arbitrary...
|
||||
if (time_remaining_ms < 0.10 * target_pause_time_ms) {
|
||||
double threshold = 0.10 * target_pause_time_ms;
|
||||
if (time_remaining_ms < threshold) {
|
||||
double prev_time_remaining_ms = time_remaining_ms;
|
||||
time_remaining_ms = 0.50 * target_pause_time_ms;
|
||||
_within_target = false;
|
||||
ergo_verbose3(ErgoCSetConstruction,
|
||||
"adjust remaining time",
|
||||
ergo_format_reason("remaining time lower than threshold")
|
||||
ergo_format_ms("remaining time")
|
||||
ergo_format_ms("threshold")
|
||||
ergo_format_ms("adjusted remaining time"),
|
||||
prev_time_remaining_ms, threshold, time_remaining_ms);
|
||||
} else {
|
||||
_within_target = true;
|
||||
}
|
||||
|
||||
// We figure out the number of bytes available for future to-space.
|
||||
// For new regions without marking information, we must assume the
|
||||
// worst-case of complete survival. If we have marking information for a
|
||||
// region, we can bound the amount of live data. We can add a number of
|
||||
// such regions, as long as the sum of the live data bounds does not
|
||||
// exceed the available evacuation space.
|
||||
size_t max_live_bytes = _g1->free_regions() * HeapRegion::GrainBytes;
|
||||
|
||||
size_t expansion_bytes =
|
||||
_g1->expansion_regions() * HeapRegion::GrainBytes;
|
||||
|
||||
_collection_set_bytes_used_before = 0;
|
||||
_collection_set_size = 0;
|
||||
|
||||
// Adjust for expansion and slop.
|
||||
max_live_bytes = max_live_bytes + expansion_bytes;
|
||||
size_t expansion_bytes = _g1->expansion_regions() * HeapRegion::GrainBytes;
|
||||
|
||||
HeapRegion* hr;
|
||||
double young_start_time_sec = os::elapsedTime();
|
||||
|
||||
if (G1PolicyVerbose > 0) {
|
||||
gclog_or_tty->print_cr("Adding %d young regions to the CSet",
|
||||
_g1->young_list()->length());
|
||||
}
|
||||
|
||||
_collection_set_bytes_used_before = 0;
|
||||
_collection_set_size = 0;
|
||||
_young_cset_length = 0;
|
||||
_last_young_gc_full = full_young_gcs() ? true : false;
|
||||
|
||||
if (_last_young_gc_full)
|
||||
if (_last_young_gc_full) {
|
||||
++_full_young_pause_num;
|
||||
else
|
||||
} else {
|
||||
++_partial_young_pause_num;
|
||||
}
|
||||
|
||||
// The young list is laid with the survivor regions from the previous
|
||||
// pause are appended to the RHS of the young list, i.e.
|
||||
// [Newly Young Regions ++ Survivors from last pause].
|
||||
|
||||
hr = _g1->young_list()->first_survivor_region();
|
||||
size_t survivor_region_num = young_list->survivor_length();
|
||||
size_t eden_region_num = young_list->length() - survivor_region_num;
|
||||
size_t old_region_num = 0;
|
||||
hr = young_list->first_survivor_region();
|
||||
while (hr != NULL) {
|
||||
assert(hr->is_survivor(), "badly formed young list");
|
||||
hr->set_young();
|
||||
hr = hr->get_next_young_region();
|
||||
}
|
||||
|
||||
// Clear the fields that point to the survivor list - they are
|
||||
// all young now.
|
||||
_g1->young_list()->clear_survivors();
|
||||
// Clear the fields that point to the survivor list - they are all young now.
|
||||
young_list->clear_survivors();
|
||||
|
||||
if (_g1->mark_in_progress())
|
||||
_g1->concurrent_mark()->register_collection_set_finger(_inc_cset_max_finger);
|
||||
@ -2831,14 +2905,17 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
_collection_set = _inc_cset_head;
|
||||
_collection_set_size = _inc_cset_size;
|
||||
_collection_set_bytes_used_before = _inc_cset_bytes_used_before;
|
||||
|
||||
// For young regions in the collection set, we assume the worst
|
||||
// case of complete survival
|
||||
max_live_bytes -= _inc_cset_size * HeapRegion::GrainBytes;
|
||||
|
||||
time_remaining_ms -= _inc_cset_predicted_elapsed_time_ms;
|
||||
predicted_pause_time_ms += _inc_cset_predicted_elapsed_time_ms;
|
||||
|
||||
ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
|
||||
"add young regions to CSet",
|
||||
ergo_format_region("eden")
|
||||
ergo_format_region("survivors")
|
||||
ergo_format_ms("predicted young region time"),
|
||||
eden_region_num, survivor_region_num,
|
||||
_inc_cset_predicted_elapsed_time_ms);
|
||||
|
||||
// The number of recorded young regions is the incremental
|
||||
// collection set's current size
|
||||
set_recorded_young_regions(_inc_cset_size);
|
||||
@ -2848,14 +2925,7 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
set_predicted_bytes_to_copy(_inc_cset_predicted_bytes_to_copy);
|
||||
#endif // PREDICTIONS_VERBOSE
|
||||
|
||||
if (G1PolicyVerbose > 0) {
|
||||
gclog_or_tty->print_cr(" Added " PTR_FORMAT " Young Regions to CS.",
|
||||
_inc_cset_size);
|
||||
gclog_or_tty->print_cr(" (" SIZE_FORMAT " KB left in heap.)",
|
||||
max_live_bytes/K);
|
||||
}
|
||||
|
||||
assert(_inc_cset_size == _g1->young_list()->length(), "Invariant");
|
||||
assert(_inc_cset_size == young_list->length(), "Invariant");
|
||||
|
||||
double young_end_time_sec = os::elapsedTime();
|
||||
_recorded_young_cset_choice_time_ms =
|
||||
@ -2869,6 +2939,8 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
NumberSeq seq;
|
||||
double avg_prediction = 100000000000000000.0; // something very large
|
||||
|
||||
size_t prev_collection_set_size = _collection_set_size;
|
||||
double prev_predicted_pause_time_ms = predicted_pause_time_ms;
|
||||
do {
|
||||
hr = _collectionSetChooser->getNextMarkedRegion(time_remaining_ms,
|
||||
avg_prediction);
|
||||
@ -2878,23 +2950,58 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
predicted_pause_time_ms += predicted_time_ms;
|
||||
add_to_collection_set(hr);
|
||||
record_non_young_cset_region(hr);
|
||||
max_live_bytes -= MIN2(hr->max_live_bytes(), max_live_bytes);
|
||||
if (G1PolicyVerbose > 0) {
|
||||
gclog_or_tty->print_cr(" (" SIZE_FORMAT " KB left in heap.)",
|
||||
max_live_bytes/K);
|
||||
}
|
||||
seq.add(predicted_time_ms);
|
||||
avg_prediction = seq.avg() + seq.sd();
|
||||
}
|
||||
should_continue =
|
||||
( hr != NULL) &&
|
||||
( (adaptive_young_list_length()) ? time_remaining_ms > 0.0
|
||||
: _collection_set_size < _young_list_fixed_length );
|
||||
|
||||
should_continue = true;
|
||||
if (hr == NULL) {
|
||||
// No need for an ergo verbose message here,
|
||||
// getNextMarkRegion() does this when it returns NULL.
|
||||
should_continue = false;
|
||||
} else {
|
||||
if (adaptive_young_list_length()) {
|
||||
if (time_remaining_ms < 0.0) {
|
||||
ergo_verbose1(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("remaining time is lower than 0")
|
||||
ergo_format_ms("remaining time"),
|
||||
time_remaining_ms);
|
||||
should_continue = false;
|
||||
}
|
||||
} else {
|
||||
if (_collection_set_size < _young_list_fixed_length) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("CSet length lower than target")
|
||||
ergo_format_region("CSet")
|
||||
ergo_format_region("young target"),
|
||||
_collection_set_size, _young_list_fixed_length);
|
||||
should_continue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (should_continue);
|
||||
|
||||
if (!adaptive_young_list_length() &&
|
||||
_collection_set_size < _young_list_fixed_length)
|
||||
_collection_set_size < _young_list_fixed_length) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"request partially-young GCs end",
|
||||
ergo_format_reason("CSet length lower than target")
|
||||
ergo_format_region("CSet")
|
||||
ergo_format_region("young target"),
|
||||
_collection_set_size, _young_list_fixed_length);
|
||||
_should_revert_to_full_young_gcs = true;
|
||||
}
|
||||
|
||||
old_region_num = _collection_set_size - prev_collection_set_size;
|
||||
|
||||
ergo_verbose2(ErgoCSetConstruction | ErgoHigh,
|
||||
"add old regions to CSet",
|
||||
ergo_format_region("old")
|
||||
ergo_format_ms("predicted old region time"),
|
||||
old_region_num,
|
||||
predicted_pause_time_ms - prev_predicted_pause_time_ms);
|
||||
}
|
||||
|
||||
stop_incremental_cset_building();
|
||||
@ -2903,6 +3010,16 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
|
||||
|
||||
end_recording_regions();
|
||||
|
||||
ergo_verbose5(ErgoCSetConstruction,
|
||||
"finish choosing CSet",
|
||||
ergo_format_region("eden")
|
||||
ergo_format_region("survivors")
|
||||
ergo_format_region("old")
|
||||
ergo_format_ms("predicted pause time")
|
||||
ergo_format_ms("target pause time"),
|
||||
eden_region_num, survivor_region_num, old_region_num,
|
||||
predicted_pause_time_ms, target_pause_time_ms);
|
||||
|
||||
double non_young_end_time_sec = os::elapsedTime();
|
||||
_recorded_non_young_cset_choice_time_ms =
|
||||
(non_young_end_time_sec - non_young_start_time_sec) * 1000.0;
|
||||
@ -2913,12 +3030,6 @@ void G1CollectorPolicy_BestRegionsFirst::record_full_collection_end() {
|
||||
_collectionSetChooser->updateAfterFullCollection();
|
||||
}
|
||||
|
||||
void G1CollectorPolicy_BestRegionsFirst::
|
||||
expand_if_possible(size_t numRegions) {
|
||||
size_t expansion_bytes = numRegions * HeapRegion::GrainBytes;
|
||||
_g1->expand(expansion_bytes);
|
||||
}
|
||||
|
||||
void G1CollectorPolicy_BestRegionsFirst::
|
||||
record_collection_pause_end() {
|
||||
G1CollectorPolicy::record_collection_pause_end();
|
||||
|
@ -493,7 +493,6 @@ public:
|
||||
|
||||
// </NEW PREDICTION>
|
||||
|
||||
public:
|
||||
void cset_regions_freed() {
|
||||
bool propagate = _last_young_gc_full && !_in_marking_window;
|
||||
_short_lived_surv_rate_group->all_surviving_words_recorded(propagate);
|
||||
@ -1045,7 +1044,7 @@ public:
|
||||
// new cycle, as long as we are not already in one. It's best if it
|
||||
// is called during a safepoint when the test whether a cycle is in
|
||||
// progress or not is stable.
|
||||
bool force_initial_mark_if_outside_cycle();
|
||||
bool force_initial_mark_if_outside_cycle(GCCause::Cause gc_cause);
|
||||
|
||||
// This is called at the very beginning of an evacuation pause (it
|
||||
// has to be the first thing that the pause does). If
|
||||
@ -1234,8 +1233,6 @@ public:
|
||||
|
||||
class G1CollectorPolicy_BestRegionsFirst: public G1CollectorPolicy {
|
||||
CollectionSetChooser* _collectionSetChooser;
|
||||
// If the estimated is less then desirable, resize if possible.
|
||||
void expand_if_possible(size_t numRegions);
|
||||
|
||||
virtual void choose_collection_set(double target_pause_time_ms);
|
||||
virtual void record_collection_pause_start(double start_time_sec,
|
||||
@ -1269,8 +1266,4 @@ inline double variance(int n, double sum_of_squares, double sum) {
|
||||
return (sum_of_squares - 2.0 * avg * sum + n_d * avg * avg) / n_d;
|
||||
}
|
||||
|
||||
// Local Variables: ***
|
||||
// c-indentation-style: gnu ***
|
||||
// End: ***
|
||||
|
||||
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTORPOLICY_HPP
|
||||
|
65
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.cpp
Normal file
65
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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/g1/g1ErgoVerbose.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
ErgoLevel G1ErgoVerbose::_level;
|
||||
bool G1ErgoVerbose::_enabled[ErgoHeuristicNum];
|
||||
|
||||
void G1ErgoVerbose::initialize() {
|
||||
set_level(ErgoLow);
|
||||
set_enabled(false);
|
||||
}
|
||||
|
||||
void G1ErgoVerbose::set_level(ErgoLevel level) {
|
||||
_level = level;
|
||||
}
|
||||
|
||||
void G1ErgoVerbose::set_enabled(ErgoHeuristic n, bool enabled) {
|
||||
assert(0 <= n && n < ErgoHeuristicNum, "pre-condition");
|
||||
_enabled[n] = enabled;
|
||||
}
|
||||
|
||||
void G1ErgoVerbose::set_enabled(bool enabled) {
|
||||
for (int n = 0; n < ErgoHeuristicNum; n += 1) {
|
||||
set_enabled((ErgoHeuristic) n, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
const char* G1ErgoVerbose::to_string(int tag) {
|
||||
ErgoHeuristic n = extract_heuristic(tag);
|
||||
switch (n) {
|
||||
case ErgoHeapSizing: return "Heap Sizing";
|
||||
case ErgoCSetConstruction: return "CSet Construction";
|
||||
case ErgoConcCycles: return "Concurrent Cycles";
|
||||
case ErgoPartiallyYoungGCs: return "Partially-Young GCs";
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
// Keep the Windows compiler happy
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
197
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.hpp
Normal file
197
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.hpp
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_G1_G1ERGOVERBOSE_HPP
|
||||
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1ERGOVERBOSE_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
// The log of G1's heuristic decisions comprises of a series of
|
||||
// records which have a similar format in order to maintain
|
||||
// consistency across records and ultimately easier parsing of the
|
||||
// output, if we ever choose to do that. Each record consists of:
|
||||
// * A time stamp to be able to easily correlate each record with
|
||||
// other events.
|
||||
// * A unique string to allow us to easily identify such records.
|
||||
// * The name of the heuristic the record corresponds to.
|
||||
// * An action string which describes the action that G1 did or is
|
||||
// about to do.
|
||||
// * An optional reason string which describes the reason for the
|
||||
// action.
|
||||
// * An optional number of name/value pairs which contributed to the
|
||||
// decision to take the action described in the record.
|
||||
//
|
||||
// Each record is associated with a "tag" which is the combination of
|
||||
// the heuristic the record corresponds to, as well as the min level
|
||||
// of verboseness at which the record should be printed. The tag is
|
||||
// checked against the current settings to determine whether the record
|
||||
// should be printed or not.
|
||||
|
||||
// The available verboseness levels.
|
||||
typedef enum {
|
||||
// Determine which part of the tag is occupied by the level.
|
||||
ErgoLevelShift = 8,
|
||||
ErgoLevelMask = ~((1 << ErgoLevelShift) - 1),
|
||||
|
||||
// ErgoLow is 0 so that we don't have to explicitly or a heuristic
|
||||
// id with ErgoLow to keep its use simpler.
|
||||
ErgoLow = 0,
|
||||
ErgoHigh = 1 << ErgoLevelShift,
|
||||
} ErgoLevel;
|
||||
|
||||
// The available heuristics.
|
||||
typedef enum {
|
||||
// Determines which part of the tag is occupied by the heuristic id.
|
||||
ErgoHeuristicMask = ~ErgoLevelMask,
|
||||
|
||||
ErgoHeapSizing = 0,
|
||||
ErgoCSetConstruction,
|
||||
ErgoConcCycles,
|
||||
ErgoPartiallyYoungGCs,
|
||||
|
||||
ErgoHeuristicNum
|
||||
} ErgoHeuristic;
|
||||
|
||||
class G1ErgoVerbose : AllStatic {
|
||||
private:
|
||||
// Determines the minimum verboseness level at which records will be
|
||||
// printed.
|
||||
static ErgoLevel _level;
|
||||
// Determines which heuristics are currently enabled.
|
||||
static bool _enabled[ErgoHeuristicNum];
|
||||
|
||||
static ErgoLevel extract_level(int tag) {
|
||||
return (ErgoLevel) (tag & ErgoLevelMask);
|
||||
}
|
||||
|
||||
static ErgoHeuristic extract_heuristic(int tag) {
|
||||
return (ErgoHeuristic) (tag & ErgoHeuristicMask);
|
||||
}
|
||||
|
||||
public:
|
||||
// Needs to be explicitly called at GC initialization.
|
||||
static void initialize();
|
||||
|
||||
static void set_level(ErgoLevel level);
|
||||
static void set_enabled(ErgoHeuristic h, bool enabled);
|
||||
// It is applied to all heuristics.
|
||||
static void set_enabled(bool enabled);
|
||||
|
||||
static bool enabled(int tag) {
|
||||
ErgoLevel level = extract_level(tag);
|
||||
ErgoHeuristic n = extract_heuristic(tag);
|
||||
return level <= _level && _enabled[n];
|
||||
}
|
||||
|
||||
// Extract the heuristic id from the tag and return a string with
|
||||
// its name.
|
||||
static const char* to_string(int tag);
|
||||
};
|
||||
|
||||
// The macros below generate the format string for values of different
|
||||
// types and/or metrics.
|
||||
|
||||
// The reason for the action is optional and is handled specially: the
|
||||
// reason string is concatenated here so it's not necessary to pass it
|
||||
// as a parameter.
|
||||
#define ergo_format_reason(_reason_) ", reason: " _reason_
|
||||
|
||||
// Single parameter format strings
|
||||
#define ergo_format_str(_name_) ", " _name_ ": %s"
|
||||
#define ergo_format_region(_name_) ", " _name_ ": "SIZE_FORMAT" regions"
|
||||
#define ergo_format_byte(_name_) ", " _name_ ": "SIZE_FORMAT" bytes"
|
||||
#define ergo_format_double(_name_) ", " _name_ ": %1.2f"
|
||||
#define ergo_format_perc(_name_) ", " _name_ ": %1.2f %%"
|
||||
#define ergo_format_ms(_name_) ", " _name_ ": %1.2f ms"
|
||||
|
||||
// Double parameter format strings
|
||||
#define ergo_format_byte_perc(_name_) \
|
||||
", " _name_ ": "SIZE_FORMAT" bytes (%1.2f %%)"
|
||||
|
||||
// Generates the format string
|
||||
#define ergo_format(_action_, _extra_format_) \
|
||||
" %1.3f: [G1Ergonomics (%s) " _action_ _extra_format_ "]"
|
||||
|
||||
// Conditionally, prints an ergonomic decision record. _extra_format_
|
||||
// is the format string for the optional items we'd like to print
|
||||
// (i.e., the decision's reason and any associated values). This
|
||||
// string should be built up using the ergo_*_format macros (see
|
||||
// above) to ensure consistency.
|
||||
//
|
||||
// Since we cannot rely on the compiler supporting variable argument
|
||||
// macros, this macro accepts a fixed number of arguments and passes
|
||||
// them to the print method. For convenience, we have wrapper macros
|
||||
// below which take a specific number of arguments and set the rest to
|
||||
// a default value.
|
||||
#define ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_) \
|
||||
do { \
|
||||
if (G1ErgoVerbose::enabled((_tag_))) { \
|
||||
gclog_or_tty->print_cr(ergo_format(_action_, _extra_format_), \
|
||||
os::elapsedTime(), \
|
||||
G1ErgoVerbose::to_string((_tag_)), \
|
||||
(_arg0_), (_arg1_), (_arg2_), \
|
||||
(_arg3_), (_arg4_), (_arg5_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define ergo_verbose(_tag_, _action_) \
|
||||
ergo_verbose_common(_tag_, _action_, "", 0, 0, 0, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose0(_tag_, _action_, _extra_format_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose1(_tag_, _action_, _extra_format_, \
|
||||
_arg0_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, 0, 0, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose2(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, 0, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose3(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose4(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, 0, 0)
|
||||
|
||||
#define ergo_verbose5(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_, 0)
|
||||
|
||||
#define ergo_verbose6(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_) \
|
||||
ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_)
|
||||
|
||||
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1ERGOVERBOSE_HPP
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2011, 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
|
||||
@ -97,10 +97,6 @@ void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) {
|
||||
// or performance (we are GC'ing most of the time anyway!),
|
||||
// simply overwrite the oldest entry in the tracker.
|
||||
|
||||
if (G1PolicyVerbose > 1) {
|
||||
warning("MMU Tracker Queue overflow. Replacing earliest entry.");
|
||||
}
|
||||
|
||||
_head_index = trim_index(_head_index + 1);
|
||||
assert(_head_index == _tail_index, "Because we have a full circular buffer");
|
||||
_tail_index = trim_index(_tail_index + 1);
|
||||
|
@ -98,7 +98,7 @@ void VM_G1IncCollectionPause::doit() {
|
||||
|
||||
// At this point we are supposed to start a concurrent cycle. We
|
||||
// will do so if one is not already in progress.
|
||||
bool res = g1h->g1_policy()->force_initial_mark_if_outside_cycle();
|
||||
bool res = g1h->g1_policy()->force_initial_mark_if_outside_cycle(_gc_cause);
|
||||
|
||||
// The above routine returns true if we were able to force the
|
||||
// next GC pause to be an initial mark; it returns false if a
|
||||
|
Loading…
Reference in New Issue
Block a user