From 4a18c97a6196fba5459fc7e261cf891dd87417c2 Mon Sep 17 00:00:00 2001 From: tonyp Date: Fri, 23 Sep 2011 16:07:49 -0400 Subject: [PATCH] 7075646: G1: fix inconsistencies in the monitoring data Summary: Fixed a few inconsistencies in the monitoring data, in particular when reported from jstat. Reviewed-by: jmasa, brutisso, johnc --- .../gc_implementation/g1/g1CollectedHeap.cpp | 17 +- .../gc_implementation/g1/g1CollectedHeap.hpp | 5 +- .../g1/g1CollectorPolicy.hpp | 4 + .../g1/g1MonitoringSupport.cpp | 223 +++++++++----- .../g1/g1MonitoringSupport.hpp | 284 +++++++++++------- .../vm/gc_implementation/g1/heapRegion.hpp | 5 + .../shared/generationCounters.cpp | 45 ++- .../shared/generationCounters.hpp | 23 +- src/share/vm/services/g1MemoryPool.cpp | 56 +--- src/share/vm/services/g1MemoryPool.hpp | 41 +-- 10 files changed, 427 insertions(+), 276 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 66ca7442c..b71f058a8 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -816,6 +816,11 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) { result = humongous_obj_allocate_initialize_regions(first, num_regions, word_size); assert(result != NULL, "it should always return a valid result"); + + // A successful humongous object allocation changes the used space + // information of the old generation so we need to recalculate the + // sizes and update the jstat counters here. + g1mm()->update_sizes(); } verify_region_sets_optional(); @@ -1422,7 +1427,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, if (PrintHeapAtGC) { Universe::print_heap_after_gc(); } - g1mm()->update_counters(); + g1mm()->update_sizes(); post_full_gc_dump(); return true; @@ -1790,6 +1795,7 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : _evac_failure_scan_stack(NULL) , _mark_in_progress(false), _cg1r(NULL), _summary_bytes_used(0), + _g1mm(NULL), _refine_cte_cl(NULL), _full_collection(false), _free_list("Master Free List"), @@ -2069,7 +2075,7 @@ jint G1CollectedHeap::initialize() { // Do create of the monitoring and management support so that // values in the heap have been properly initialized. - _g1mm = new G1MonitoringSupport(this, &_g1_storage); + _g1mm = new G1MonitoringSupport(this); return JNI_OK; } @@ -3702,7 +3708,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { if (PrintHeapAtGC) { Universe::print_heap_after_gc(); } - g1mm()->update_counters(); + g1mm()->update_sizes(); if (G1SummarizeRSetStats && (G1SummarizeRSetStatsPeriod > 0) && @@ -5815,7 +5821,6 @@ HeapRegion* G1CollectedHeap::new_mutator_alloc_region(size_t word_size, g1_policy()->update_region_num(true /* next_is_young */); set_region_short_lived_locked(new_alloc_region); _hr_printer.alloc(new_alloc_region, G1HRPrinter::Eden, young_list_full); - g1mm()->update_eden_counters(); return new_alloc_region; } } @@ -5830,6 +5835,10 @@ void G1CollectedHeap::retire_mutator_alloc_region(HeapRegion* alloc_region, g1_policy()->add_region_to_incremental_cset_lhs(alloc_region); _summary_bytes_used += allocated_bytes; _hr_printer.retire(alloc_region); + // We update the eden sizes here, when the region is retired, + // instead of when it's allocated, since this is the point that its + // used space has been recored in _summary_bytes_used. + g1mm()->update_eden_size(); } HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size, diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 9dcf1a825..76bf194a9 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -597,7 +597,10 @@ protected: public: - G1MonitoringSupport* g1mm() { return _g1mm; } + G1MonitoringSupport* g1mm() { + assert(_g1mm != NULL, "should have been initialized"); + return _g1mm; + } // Expand the garbage-first heap by at least the given size (in bytes!). // Returns true if the heap was expanded by the requested amount; diff --git a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp index 2c315729e..4e6139518 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp @@ -1149,6 +1149,10 @@ public: return young_list_length < young_list_max_length; } + size_t young_list_max_length() { + return _young_list_max_length; + } + void update_region_num(bool young); bool full_young_gcs() { diff --git a/src/share/vm/gc_implementation/g1/g1MonitoringSupport.cpp b/src/share/vm/gc_implementation/g1/g1MonitoringSupport.cpp index aab16dba6..d22dc7a35 100644 --- a/src/share/vm/gc_implementation/g1/g1MonitoringSupport.cpp +++ b/src/share/vm/gc_implementation/g1/g1MonitoringSupport.cpp @@ -27,19 +27,69 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1CollectorPolicy.hpp" -G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h, - VirtualSpace* g1_storage_addr) : +G1GenerationCounters::G1GenerationCounters(G1MonitoringSupport* g1mm, + const char* name, + int ordinal, int spaces, + size_t min_capacity, + size_t max_capacity, + size_t curr_capacity) + : GenerationCounters(name, ordinal, spaces, min_capacity, + max_capacity, curr_capacity), _g1mm(g1mm) { } + +// We pad the capacity three times given that the young generation +// contains three spaces (eden and two survivors). +G1YoungGenerationCounters::G1YoungGenerationCounters(G1MonitoringSupport* g1mm, + const char* name) + : G1GenerationCounters(g1mm, name, 0 /* ordinal */, 3 /* spaces */, + G1MonitoringSupport::pad_capacity(0, 3) /* min_capacity */, + G1MonitoringSupport::pad_capacity(g1mm->young_gen_max(), 3), + G1MonitoringSupport::pad_capacity(0, 3) /* curr_capacity */) { + update_all(); +} + +G1OldGenerationCounters::G1OldGenerationCounters(G1MonitoringSupport* g1mm, + const char* name) + : G1GenerationCounters(g1mm, name, 1 /* ordinal */, 1 /* spaces */, + G1MonitoringSupport::pad_capacity(0) /* min_capacity */, + G1MonitoringSupport::pad_capacity(g1mm->old_gen_max()), + G1MonitoringSupport::pad_capacity(0) /* curr_capacity */) { + update_all(); +} + +void G1YoungGenerationCounters::update_all() { + size_t committed = + G1MonitoringSupport::pad_capacity(_g1mm->young_gen_committed(), 3); + _current_size->set_value(committed); +} + +void G1OldGenerationCounters::update_all() { + size_t committed = + G1MonitoringSupport::pad_capacity(_g1mm->old_gen_committed()); + _current_size->set_value(committed); +} + +G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) : _g1h(g1h), _incremental_collection_counters(NULL), _full_collection_counters(NULL), - _non_young_collection_counters(NULL), + _old_collection_counters(NULL), _old_space_counters(NULL), _young_collection_counters(NULL), _eden_counters(NULL), _from_counters(NULL), _to_counters(NULL), - _g1_storage_addr(g1_storage_addr) -{ + + _overall_reserved(0), + _overall_committed(0), _overall_used(0), + _young_region_num(0), + _young_gen_committed(0), + _eden_committed(0), _eden_used(0), + _survivor_committed(0), _survivor_used(0), + _old_committed(0), _old_used(0) { + + _overall_reserved = g1h->max_capacity(); + recalculate_sizes(); + // Counters for GC collections // // name "collector.0". In a generational collector this would be the @@ -69,110 +119,147 @@ G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h, // generational GC terms. The "1, 1" parameters are for // the n-th generation (=1) with 1 space. // Counters are created from minCapacity, maxCapacity, and capacity - _non_young_collection_counters = - new GenerationCounters("whole heap", 1, 1, _g1_storage_addr); + _old_collection_counters = new G1OldGenerationCounters(this, "old"); // name "generation.1.space.0" // Counters are created from maxCapacity, capacity, initCapacity, // and used. - _old_space_counters = new HSpaceCounters("space", 0, - _g1h->max_capacity(), _g1h->capacity(), _non_young_collection_counters); + _old_space_counters = new HSpaceCounters("space", 0 /* ordinal */, + pad_capacity(overall_reserved()) /* max_capacity */, + pad_capacity(old_space_committed()) /* init_capacity */, + _old_collection_counters); // Young collection set // name "generation.0". This is logically the young generation. // The "0, 3" are paremeters for the n-th genertaion (=0) with 3 spaces. - // See _non_young_collection_counters for additional counters - _young_collection_counters = new GenerationCounters("young", 0, 3, NULL); + // See _old_collection_counters for additional counters + _young_collection_counters = new G1YoungGenerationCounters(this, "young"); - // Replace "max_heap_byte_size() with maximum young gen size for - // g1Collectedheap // name "generation.0.space.0" // See _old_space_counters for additional counters - _eden_counters = new HSpaceCounters("eden", 0, - _g1h->max_capacity(), eden_space_committed(), + _eden_counters = new HSpaceCounters("eden", 0 /* ordinal */, + pad_capacity(overall_reserved()) /* max_capacity */, + pad_capacity(eden_space_committed()) /* init_capacity */, _young_collection_counters); // name "generation.0.space.1" // See _old_space_counters for additional counters // Set the arguments to indicate that this survivor space is not used. - _from_counters = new HSpaceCounters("s0", 1, (long) 0, (long) 0, + _from_counters = new HSpaceCounters("s0", 1 /* ordinal */, + pad_capacity(0) /* max_capacity */, + pad_capacity(0) /* init_capacity */, _young_collection_counters); + // Given that this survivor space is not used, we update it here + // once to reflect that its used space is 0 so that we don't have to + // worry about updating it again later. + _from_counters->update_used(0); // name "generation.0.space.2" // See _old_space_counters for additional counters - _to_counters = new HSpaceCounters("s1", 2, - _g1h->max_capacity(), - survivor_space_committed(), + _to_counters = new HSpaceCounters("s1", 2 /* ordinal */, + pad_capacity(overall_reserved()) /* max_capacity */, + pad_capacity(survivor_space_committed()) /* init_capacity */, _young_collection_counters); } -size_t G1MonitoringSupport::overall_committed() { - return g1h()->capacity(); -} +void G1MonitoringSupport::recalculate_sizes() { + G1CollectedHeap* g1 = g1h(); -size_t G1MonitoringSupport::overall_used() { - return g1h()->used_unlocked(); -} + // Recalculate all the sizes from scratch. We assume that this is + // called at a point where no concurrent updates to the various + // values we read here are possible (i.e., at a STW phase at the end + // of a GC). -size_t G1MonitoringSupport::eden_space_committed() { - return MAX2(eden_space_used(), (size_t) HeapRegion::GrainBytes); -} + size_t young_list_length = g1->young_list()->length(); + size_t survivor_list_length = g1->g1_policy()->recorded_survivor_regions(); + assert(young_list_length >= survivor_list_length, "invariant"); + size_t eden_list_length = young_list_length - survivor_list_length; + // Max length includes any potential extensions to the young gen + // we'll do when the GC locker is active. + size_t young_list_max_length = g1->g1_policy()->young_list_max_length(); + assert(young_list_max_length >= survivor_list_length, "invariant"); + size_t eden_list_max_length = young_list_max_length - survivor_list_length; -size_t G1MonitoringSupport::eden_space_used() { - size_t young_list_length = g1h()->young_list()->length(); - size_t eden_used = young_list_length * HeapRegion::GrainBytes; - size_t survivor_used = survivor_space_used(); - eden_used = subtract_up_to_zero(eden_used, survivor_used); - return eden_used; -} + _overall_used = g1->used_unlocked(); + _eden_used = eden_list_length * HeapRegion::GrainBytes; + _survivor_used = survivor_list_length * HeapRegion::GrainBytes; + _young_region_num = young_list_length; + _old_used = subtract_up_to_zero(_overall_used, _eden_used + _survivor_used); -size_t G1MonitoringSupport::survivor_space_committed() { - return MAX2(survivor_space_used(), - (size_t) HeapRegion::GrainBytes); -} + // First calculate the committed sizes that can be calculated independently. + _survivor_committed = _survivor_used; + _old_committed = HeapRegion::align_up_to_region_byte_size(_old_used); -size_t G1MonitoringSupport::survivor_space_used() { - size_t survivor_num = g1h()->g1_policy()->recorded_survivor_regions(); - size_t survivor_used = survivor_num * HeapRegion::GrainBytes; - return survivor_used; -} + // Next, start with the overall committed size. + _overall_committed = g1->capacity(); + size_t committed = _overall_committed; -size_t G1MonitoringSupport::old_space_committed() { - size_t committed = overall_committed(); - size_t eden_committed = eden_space_committed(); - size_t survivor_committed = survivor_space_committed(); - committed = subtract_up_to_zero(committed, eden_committed); - committed = subtract_up_to_zero(committed, survivor_committed); - committed = MAX2(committed, (size_t) HeapRegion::GrainBytes); - return committed; + // Remove the committed size we have calculated so far (for the + // survivor and old space). + assert(committed >= (_survivor_committed + _old_committed), "sanity"); + committed -= _survivor_committed + _old_committed; + + // Next, calculate and remove the committed size for the eden. + _eden_committed = eden_list_max_length * HeapRegion::GrainBytes; + // Somewhat defensive: be robust in case there are inaccuracies in + // the calculations + _eden_committed = MIN2(_eden_committed, committed); + committed -= _eden_committed; + + // Finally, give the rest to the old space... + _old_committed += committed; + // ..and calculate the young gen committed. + _young_gen_committed = _eden_committed + _survivor_committed; + + assert(_overall_committed == + (_eden_committed + _survivor_committed + _old_committed), + "the committed sizes should add up"); + // Somewhat defensive: cap the eden used size to make sure it + // never exceeds the committed size. + _eden_used = MIN2(_eden_used, _eden_committed); + // _survivor_committed and _old_committed are calculated in terms of + // the corresponding _*_used value, so the next two conditions + // should hold. + assert(_survivor_used <= _survivor_committed, "post-condition"); + assert(_old_used <= _old_committed, "post-condition"); } -// See the comment near the top of g1MonitoringSupport.hpp for -// an explanation of these calculations for "used" and "capacity". -size_t G1MonitoringSupport::old_space_used() { - size_t used = overall_used(); - size_t eden_used = eden_space_used(); - size_t survivor_used = survivor_space_used(); - used = subtract_up_to_zero(used, eden_used); - used = subtract_up_to_zero(used, survivor_used); - return used; +void G1MonitoringSupport::recalculate_eden_size() { + G1CollectedHeap* g1 = g1h(); + + // When a new eden region is allocated, only the eden_used size is + // affected (since we have recalculated everything else at the last GC). + + size_t young_region_num = g1h()->young_list()->length(); + if (young_region_num > _young_region_num) { + size_t diff = young_region_num - _young_region_num; + _eden_used += diff * HeapRegion::GrainBytes; + // Somewhat defensive: cap the eden used size to make sure it + // never exceeds the committed size. + _eden_used = MIN2(_eden_used, _eden_committed); + _young_region_num = young_region_num; + } } -void G1MonitoringSupport::update_counters() { +void G1MonitoringSupport::update_sizes() { + recalculate_sizes(); if (UsePerfData) { - eden_counters()->update_capacity(eden_space_committed()); + eden_counters()->update_capacity(pad_capacity(eden_space_committed())); eden_counters()->update_used(eden_space_used()); - to_counters()->update_capacity(survivor_space_committed()); + // only the to survivor space (s1) is active, so we don't need to + // update the counteres for the from survivor space (s0) + to_counters()->update_capacity(pad_capacity(survivor_space_committed())); to_counters()->update_used(survivor_space_used()); - old_space_counters()->update_capacity(old_space_committed()); + old_space_counters()->update_capacity(pad_capacity(old_space_committed())); old_space_counters()->update_used(old_space_used()); - non_young_collection_counters()->update_all(); + old_collection_counters()->update_all(); + young_collection_counters()->update_all(); } } -void G1MonitoringSupport::update_eden_counters() { +void G1MonitoringSupport::update_eden_size() { + recalculate_eden_size(); if (UsePerfData) { - eden_counters()->update_capacity(eden_space_committed()); eden_counters()->update_used(eden_space_used()); } } diff --git a/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp b/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp index 61de266fe..aa142916b 100644 --- a/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp +++ b/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp @@ -28,101 +28,93 @@ #include "gc_implementation/shared/hSpaceCounters.hpp" class G1CollectedHeap; -class G1SpaceMonitoringSupport; - -// Class for monitoring logical spaces in G1. -// G1 defines a set of regions as a young -// collection (analogous to a young generation). -// The young collection is a logical generation -// with no fixed chunk (see space.hpp) reflecting -// the address space for the generation. In addition -// to the young collection there is its complement -// the non-young collection that is simply the regions -// not in the young collection. The non-young collection -// is treated here as a logical old generation only -// because the monitoring tools expect a generational -// heap. The monitoring tools expect that a Space -// (see space.hpp) exists that describe the -// address space of young collection and non-young -// collection and such a view is provided here. -// -// This class provides interfaces to access -// the value of variables for the young collection -// that include the "capacity" and "used" of the -// young collection along with constant values -// for the minimum and maximum capacities for -// the logical spaces. Similarly for the non-young -// collection. -// -// Also provided are counters for G1 concurrent collections -// and stop-the-world full heap collecitons. -// -// Below is a description of how "used" and "capactiy" -// (or committed) is calculated for the logical spaces. -// -// 1) The used space calculation for a pool is not necessarily -// independent of the others. We can easily get from G1 the overall -// used space in the entire heap, the number of regions in the young -// generation (includes both eden and survivors), and the number of -// survivor regions. So, from that we calculate: -// -// survivor_used = survivor_num * region_size -// eden_used = young_region_num * region_size - survivor_used -// old_gen_used = overall_used - eden_used - survivor_used -// -// Note that survivor_used and eden_used are upper bounds. To get the -// actual value we would have to iterate over the regions and add up -// ->used(). But that'd be expensive. So, we'll accept some lack of -// accuracy for those two. But, we have to be careful when calculating -// old_gen_used, in case we subtract from overall_used more then the -// actual number and our result goes negative. -// -// 2) Calculating the used space is straightforward, as described -// above. However, how do we calculate the committed space, given that -// we allocate space for the eden, survivor, and old gen out of the -// same pool of regions? One way to do this is to use the used value -// as also the committed value for the eden and survivor spaces and -// then calculate the old gen committed space as follows: -// -// old_gen_committed = overall_committed - eden_committed - survivor_committed -// -// Maybe a better way to do that would be to calculate used for eden -// and survivor as a sum of ->used() over their regions and then -// calculate committed as region_num * region_size (i.e., what we use -// to calculate the used space now). This is something to consider -// in the future. -// -// 3) Another decision that is again not straightforward is what is -// the max size that each memory pool can grow to. One way to do this -// would be to use the committed size for the max for the eden and -// survivors and calculate the old gen max as follows (basically, it's -// a similar pattern to what we use for the committed space, as -// described above): -// -// old_gen_max = overall_max - eden_max - survivor_max -// -// Unfortunately, the above makes the max of each pool fluctuate over -// time and, even though this is allowed according to the spec, it -// broke several assumptions in the M&M framework (there were cases -// where used would reach a value greater than max). So, for max we -// use -1, which means "undefined" according to the spec. -// -// 4) Now, there is a very subtle issue with all the above. The -// framework will call get_memory_usage() on the three pools -// asynchronously. As a result, each call might get a different value -// for, say, survivor_num which will yield inconsistent values for -// eden_used, survivor_used, and old_gen_used (as survivor_num is used -// in the calculation of all three). This would normally be -// ok. However, it's possible that this might cause the sum of -// eden_used, survivor_used, and old_gen_used to go over the max heap -// size and this seems to sometimes cause JConsole (and maybe other -// clients) to get confused. There's not a really an easy / clean -// solution to this problem, due to the asynchrounous nature of the -// framework. + +// Class for monitoring logical spaces in G1. It provides data for +// both G1's jstat counters as well as G1's memory pools. +// +// G1 splits the heap into heap regions and each heap region belongs +// to one of the following categories: +// +// * eden : regions that have been allocated since the last GC +// * survivors : regions with objects that survived the last few GCs +// * old : long-lived non-humongous regions +// * humongous : humongous regions +// * free : free regions +// +// The combination of eden and survivor regions form the equivalent of +// the young generation in the other GCs. The combination of old and +// humongous regions form the equivalent of the old generation in the +// other GCs. Free regions do not have a good equivalent in the other +// GCs given that they can be allocated as any of the other region types. +// +// The monitoring tools expect the heap to contain a number of +// generations (young, old, perm) and each generation to contain a +// number of spaces (young: eden, survivors, old). Given that G1 does +// not maintain those spaces physically (e.g., the set of +// non-contiguous eden regions can be considered as a "logical" +// space), we'll provide the illusion that those generations and +// spaces exist. In reality, each generation and space refers to a set +// of heap regions that are potentially non-contiguous. +// +// This class provides interfaces to access the min, current, and max +// capacity and current occupancy for each of G1's logical spaces and +// generations we expose to the monitoring tools. Also provided are +// counters for G1 concurrent collections and stop-the-world full heap +// collections. +// +// Below is a description of how the various sizes are calculated. +// +// * Current Capacity +// +// - heap_capacity = current heap capacity (e.g., current committed size) +// - young_gen_capacity = current max young gen target capacity +// (i.e., young gen target capacity + max allowed expansion capacity) +// - survivor_capacity = current survivor region capacity +// - eden_capacity = young_gen_capacity - survivor_capacity +// - old_capacity = heap_capacity - young_gen_capacity +// +// What we do in the above is to distribute the free regions among +// eden_capacity and old_capacity. +// +// * Occupancy +// +// - young_gen_used = current young region capacity +// - survivor_used = survivor_capacity +// - eden_used = young_gen_used - survivor_used +// - old_used = overall_used - young_gen_used +// +// Unfortunately, we currently only keep track of the number of +// currently allocated young and survivor regions + the overall used +// bytes in the heap, so the above can be a little inaccurate. +// +// * Min Capacity +// +// We set this to 0 for all spaces. We could consider setting the old +// min capacity to the min capacity of the heap (see 7078465). +// +// * Max Capacity +// +// For jstat, we set the max capacity of all spaces to heap_capacity, +// given that we don't always have a reasonably upper bound on how big +// each space can grow. For the memory pools, we actually make the max +// capacity undefined. We could consider setting the old max capacity +// to the max capacity of the heap (see 7078465). +// +// If we had more accurate occupancy / capacity information per +// region set the above calculations would be greatly simplified and +// be made more accurate. +// +// We update all the above synchronously and we store the results in +// fields so that we just read said fields when needed. A subtle point +// is that all the above sizes need to be recalculated when the old +// gen changes capacity (after a GC or after a humongous allocation) +// but only the eden occupancy changes when a new eden region is +// allocated. So, in the latter case we have minimal recalcuation to +// do which is important as we want to keep the eden region allocation +// path as low-overhead as possible. class G1MonitoringSupport : public CHeapObj { G1CollectedHeap* _g1h; - VirtualSpace* _g1_storage_addr; // jstat performance counters // incremental collections both fully and partially young @@ -133,9 +125,9 @@ class G1MonitoringSupport : public CHeapObj { // _from_counters, and _to_counters are associated with // this "generational" counter. GenerationCounters* _young_collection_counters; - // non-young collection set counters. The _old_space_counters + // old collection set counters. The _old_space_counters // below are associated with this "generational" counter. - GenerationCounters* _non_young_collection_counters; + GenerationCounters* _old_collection_counters; // Counters for the capacity and used for // the whole heap HSpaceCounters* _old_space_counters; @@ -145,6 +137,27 @@ class G1MonitoringSupport : public CHeapObj { HSpaceCounters* _from_counters; HSpaceCounters* _to_counters; + // When it's appropriate to recalculate the various sizes (at the + // end of a GC, when a new eden region is allocated, etc.) we store + // them here so that we can easily report them when needed and not + // have to recalculate them every time. + + size_t _overall_reserved; + size_t _overall_committed; + size_t _overall_used; + + size_t _young_region_num; + size_t _young_gen_committed; + size_t _eden_committed; + size_t _eden_used; + size_t _survivor_committed; + size_t _survivor_used; + + size_t _old_committed; + size_t _old_used; + + G1CollectedHeap* g1h() { return _g1h; } + // It returns x - y if x > y, 0 otherwise. // As described in the comment above, some of the inputs to the // calculations we have to do are obtained concurrently and hence @@ -160,15 +173,35 @@ class G1MonitoringSupport : public CHeapObj { } } + // Recalculate all the sizes. + void recalculate_sizes(); + // Recalculate only what's necessary when a new eden region is allocated. + void recalculate_eden_size(); + public: - G1MonitoringSupport(G1CollectedHeap* g1h, VirtualSpace* g1_storage_addr); + G1MonitoringSupport(G1CollectedHeap* g1h); - G1CollectedHeap* g1h() { return _g1h; } - VirtualSpace* g1_storage_addr() { return _g1_storage_addr; } + // Unfortunately, the jstat tool assumes that no space has 0 + // capacity. In our case, given that each space is logical, it's + // possible that no regions will be allocated to it, hence to have 0 + // capacity (e.g., if there are no survivor regions, the survivor + // space has 0 capacity). The way we deal with this is to always pad + // each capacity value we report to jstat by a very small amount to + // make sure that it's never zero. Given that we sometimes have to + // report a capacity of a generation that contains several spaces + // (e.g., young gen includes one eden, two survivor spaces), the + // mult parameter is provided in order to adding the appropriate + // padding multiple times so that the capacities add up correctly. + static size_t pad_capacity(size_t size_bytes, size_t mult = 1) { + return size_bytes + MinObjAlignmentInBytes * mult; + } - // Performance Counter accessors - void update_counters(); - void update_eden_counters(); + // Recalculate all the sizes from scratch and update all the jstat + // counters accordingly. + void update_sizes(); + // Recalculate only what's necessary when a new eden region is + // allocated and update any jstat counters that need to be updated. + void update_eden_size(); CollectorCounters* incremental_collection_counters() { return _incremental_collection_counters; @@ -176,8 +209,11 @@ class G1MonitoringSupport : public CHeapObj { CollectorCounters* full_collection_counters() { return _full_collection_counters; } - GenerationCounters* non_young_collection_counters() { - return _non_young_collection_counters; + GenerationCounters* young_collection_counters() { + return _young_collection_counters; + } + GenerationCounters* old_collection_counters() { + return _old_collection_counters; } HSpaceCounters* old_space_counters() { return _old_space_counters; } HSpaceCounters* eden_counters() { return _eden_counters; } @@ -187,17 +223,45 @@ class G1MonitoringSupport : public CHeapObj { // Monitoring support used by // MemoryService // jstat counters - size_t overall_committed(); - size_t overall_used(); - size_t eden_space_committed(); - size_t eden_space_used(); + size_t overall_reserved() { return _overall_reserved; } + size_t overall_committed() { return _overall_committed; } + size_t overall_used() { return _overall_used; } + + size_t young_gen_committed() { return _young_gen_committed; } + size_t young_gen_max() { return overall_reserved(); } + size_t eden_space_committed() { return _eden_committed; } + size_t eden_space_used() { return _eden_used; } + size_t survivor_space_committed() { return _survivor_committed; } + size_t survivor_space_used() { return _survivor_used; } + + size_t old_gen_committed() { return old_space_committed(); } + size_t old_gen_max() { return overall_reserved(); } + size_t old_space_committed() { return _old_committed; } + size_t old_space_used() { return _old_used; } +}; - size_t survivor_space_committed(); - size_t survivor_space_used(); +class G1GenerationCounters: public GenerationCounters { +protected: + G1MonitoringSupport* _g1mm; + +public: + G1GenerationCounters(G1MonitoringSupport* g1mm, + const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, + size_t curr_capacity); +}; + +class G1YoungGenerationCounters: public G1GenerationCounters { +public: + G1YoungGenerationCounters(G1MonitoringSupport* g1mm, const char* name); + virtual void update_all(); +}; - size_t old_space_committed(); - size_t old_space_used(); +class G1OldGenerationCounters: public G1GenerationCounters { +public: + G1OldGenerationCounters(G1MonitoringSupport* g1mm, const char* name); + virtual void update_all(); }; #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1MONITORINGSUPPORT_HPP diff --git a/src/share/vm/gc_implementation/g1/heapRegion.hpp b/src/share/vm/gc_implementation/g1/heapRegion.hpp index f2edf01b9..2a3033152 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -357,6 +357,11 @@ class HeapRegion: public G1OffsetTableContigSpace { static int GrainWords; static int CardsPerRegion; + static size_t align_up_to_region_byte_size(size_t sz) { + return (sz + (size_t) GrainBytes - 1) & + ~((1 << (size_t) LogOfHRGrainBytes) - 1); + } + // It sets up the heap region size (GrainBytes / GrainWords), as // well as other related fields that are based on the heap region // size (LogOfHRGrainBytes / LogOfHRGrainWords / diff --git a/src/share/vm/gc_implementation/shared/generationCounters.cpp b/src/share/vm/gc_implementation/shared/generationCounters.cpp index f07f486e8..68ab6ffc1 100644 --- a/src/share/vm/gc_implementation/shared/generationCounters.cpp +++ b/src/share/vm/gc_implementation/shared/generationCounters.cpp @@ -26,14 +26,10 @@ #include "gc_implementation/shared/generationCounters.hpp" #include "memory/resourceArea.hpp" - -GenerationCounters::GenerationCounters(const char* name, - int ordinal, int spaces, - VirtualSpace* v): - _virtual_space(v) { - +void GenerationCounters::initialize(const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, + size_t curr_capacity) { if (UsePerfData) { - EXCEPTION_MARK; ResourceMark rm; @@ -51,18 +47,37 @@ GenerationCounters::GenerationCounters(const char* name, cname = PerfDataManager::counter_name(_name_space, "minCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _virtual_space == NULL ? 0 : - _virtual_space->committed_size(), CHECK); + min_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _virtual_space == NULL ? 0 : - _virtual_space->reserved_size(), CHECK); + max_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "capacity"); - _current_size = PerfDataManager::create_variable(SUN_GC, cname, - PerfData::U_Bytes, - _virtual_space == NULL ? 0 : - _virtual_space->committed_size(), CHECK); + _current_size = + PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, + curr_capacity, CHECK); } } + +GenerationCounters::GenerationCounters(const char* name, + int ordinal, int spaces, + VirtualSpace* v) + : _virtual_space(v) { + assert(v != NULL, "don't call this constructor if v == NULL"); + initialize(name, ordinal, spaces, + v->committed_size(), v->reserved_size(), v->committed_size()); +} + +GenerationCounters::GenerationCounters(const char* name, + int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, + size_t curr_capacity) + : _virtual_space(NULL) { + initialize(name, ordinal, spaces, min_capacity, max_capacity, curr_capacity); +} + +void GenerationCounters::update_all() { + assert(_virtual_space != NULL, "otherwise, override this method"); + _current_size->set_value(_virtual_space->committed_size()); +} diff --git a/src/share/vm/gc_implementation/shared/generationCounters.hpp b/src/share/vm/gc_implementation/shared/generationCounters.hpp index 21f76918c..f399b9558 100644 --- a/src/share/vm/gc_implementation/shared/generationCounters.hpp +++ b/src/share/vm/gc_implementation/shared/generationCounters.hpp @@ -34,6 +34,11 @@ class GenerationCounters: public CHeapObj { friend class VMStructs; +private: + void initialize(const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, + size_t curr_capacity); + protected: PerfVariable* _current_size; VirtualSpace* _virtual_space; @@ -48,11 +53,18 @@ class GenerationCounters: public CHeapObj { char* _name_space; // This constructor is only meant for use with the PSGenerationCounters - // constructor. The need for such an constructor should be eliminated + // constructor. The need for such an constructor should be eliminated // when VirtualSpace and PSVirtualSpace are unified. - GenerationCounters() : _name_space(NULL), _current_size(NULL), _virtual_space(NULL) {} - public: + GenerationCounters() + : _name_space(NULL), _current_size(NULL), _virtual_space(NULL) {} + + // This constructor is used for subclasses that do not have a space + // associated with them (e.g, in G1). + GenerationCounters(const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, + size_t curr_capacity); + public: GenerationCounters(const char* name, int ordinal, int spaces, VirtualSpace* v); @@ -60,10 +72,7 @@ class GenerationCounters: public CHeapObj { if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); } - virtual void update_all() { - _current_size->set_value(_virtual_space == NULL ? 0 : - _virtual_space->committed_size()); - } + virtual void update_all(); const char* name_space() const { return _name_space; } diff --git a/src/share/vm/services/g1MemoryPool.cpp b/src/share/vm/services/g1MemoryPool.cpp index a480c12b4..c621ecc79 100644 --- a/src/share/vm/services/g1MemoryPool.cpp +++ b/src/share/vm/services/g1MemoryPool.cpp @@ -32,56 +32,28 @@ G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h, const char* name, size_t init_size, + size_t max_size, bool support_usage_threshold) : - _g1h(g1h), CollectedMemoryPool(name, - MemoryPool::Heap, - init_size, - undefined_max(), - support_usage_threshold) { + _g1mm(g1h->g1mm()), CollectedMemoryPool(name, + MemoryPool::Heap, + init_size, + max_size, + support_usage_threshold) { assert(UseG1GC, "sanity"); } -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::eden_space_committed(G1CollectedHeap* g1h) { - return MAX2(eden_space_used(g1h), (size_t) HeapRegion::GrainBytes); -} - -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::eden_space_used(G1CollectedHeap* g1h) { - return g1h->g1mm()->eden_space_used(); -} - -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::survivor_space_committed(G1CollectedHeap* g1h) { - return g1h->g1mm()->survivor_space_committed(); -} - -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::survivor_space_used(G1CollectedHeap* g1h) { - return g1h->g1mm()->survivor_space_used(); -} - -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::old_space_committed(G1CollectedHeap* g1h) { - return g1h->g1mm()->old_space_committed(); -} - -// See the comment at the top of g1MemoryPool.hpp -size_t G1MemoryPoolSuper::old_space_used(G1CollectedHeap* g1h) { - return g1h->g1mm()->old_space_used(); -} - G1EdenPool::G1EdenPool(G1CollectedHeap* g1h) : G1MemoryPoolSuper(g1h, "G1 Eden Space", - eden_space_committed(g1h), /* init_size */ + g1h->g1mm()->eden_space_committed(), /* init_size */ + _undefined_max, false /* support_usage_threshold */) { } MemoryUsage G1EdenPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = eden_space_committed(_g1h); + size_t committed = _g1mm->eden_space_committed(); return MemoryUsage(initial_sz, used, committed, max_sz); } @@ -89,14 +61,15 @@ MemoryUsage G1EdenPool::get_memory_usage() { G1SurvivorPool::G1SurvivorPool(G1CollectedHeap* g1h) : G1MemoryPoolSuper(g1h, "G1 Survivor Space", - survivor_space_committed(g1h), /* init_size */ + g1h->g1mm()->survivor_space_committed(), /* init_size */ + _undefined_max, false /* support_usage_threshold */) { } MemoryUsage G1SurvivorPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = survivor_space_committed(_g1h); + size_t committed = _g1mm->survivor_space_committed(); return MemoryUsage(initial_sz, used, committed, max_sz); } @@ -104,14 +77,15 @@ MemoryUsage G1SurvivorPool::get_memory_usage() { G1OldGenPool::G1OldGenPool(G1CollectedHeap* g1h) : G1MemoryPoolSuper(g1h, "G1 Old Gen", - old_space_committed(g1h), /* init_size */ + g1h->g1mm()->old_space_committed(), /* init_size */ + _undefined_max, true /* support_usage_threshold */) { } MemoryUsage G1OldGenPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = old_space_committed(_g1h); + size_t committed = _g1mm->old_space_committed(); return MemoryUsage(initial_sz, used, committed, max_sz); } diff --git a/src/share/vm/services/g1MemoryPool.hpp b/src/share/vm/services/g1MemoryPool.hpp index 958a0885b..ce93d0e5a 100644 --- a/src/share/vm/services/g1MemoryPool.hpp +++ b/src/share/vm/services/g1MemoryPool.hpp @@ -26,12 +26,11 @@ #define SHARE_VM_SERVICES_G1MEMORYPOOL_HPP #ifndef SERIALGC +#include "gc_implementation/g1/g1MonitoringSupport.hpp" #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" #endif -class G1CollectedHeap; - // This file contains the three classes that represent the memory // pools of the G1 spaces: G1EdenPool, G1SurvivorPool, and // G1OldGenPool. In G1, unlike our other GCs, we do not have a @@ -50,37 +49,19 @@ class G1CollectedHeap; // on this model. // - // This class is shared by the three G1 memory pool classes -// (G1EdenPool, G1SurvivorPool, G1OldGenPool). Given that the way we -// calculate used / committed bytes for these three pools is related -// (see comment above), we put the calculations in this class so that -// we can easily share them among the subclasses. +// (G1EdenPool, G1SurvivorPool, G1OldGenPool). class G1MemoryPoolSuper : public CollectedMemoryPool { protected: - G1CollectedHeap* _g1h; + const static size_t _undefined_max = (size_t) -1; + G1MonitoringSupport* _g1mm; // Would only be called from subclasses. G1MemoryPoolSuper(G1CollectedHeap* g1h, const char* name, size_t init_size, + size_t max_size, bool support_usage_threshold); - - // The reason why all the code is in static methods is so that it - // can be safely called from the constructors of the subclasses. - - static size_t undefined_max() { - return (size_t) -1; - } - - static size_t eden_space_committed(G1CollectedHeap* g1h); - static size_t eden_space_used(G1CollectedHeap* g1h); - - static size_t survivor_space_committed(G1CollectedHeap* g1h); - static size_t survivor_space_used(G1CollectedHeap* g1h); - - static size_t old_space_committed(G1CollectedHeap* g1h); - static size_t old_space_used(G1CollectedHeap* g1h); }; // Memory pool that represents the G1 eden. @@ -89,10 +70,10 @@ public: G1EdenPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return eden_space_used(_g1h); + return _g1mm->eden_space_used(); } size_t max_size() const { - return undefined_max(); + return _undefined_max; } MemoryUsage get_memory_usage(); }; @@ -103,10 +84,10 @@ public: G1SurvivorPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return survivor_space_used(_g1h); + return _g1mm->survivor_space_used(); } size_t max_size() const { - return undefined_max(); + return _undefined_max; } MemoryUsage get_memory_usage(); }; @@ -117,10 +98,10 @@ public: G1OldGenPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return old_space_used(_g1h); + return _g1mm->old_space_used(); } size_t max_size() const { - return undefined_max(); + return _undefined_max; } MemoryUsage get_memory_usage(); }; -- GitLab