# HG changeset patch # User tonyp # Date 1308933529 14400 # Node ID 5f6f2615433a9682c393b902bb7719eb41781428 # Parent e8b0b03920378f2df94468b7a403a11b45f5efc4 7049999: G1: Make the G1PrintHeapRegions output consistent and complete Summary: Extend and make more consistent the output from the G1PrintHeapRegions flag. Reviewed-by: johnc, jmasa diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Tue Jun 21 15:23:07 2011 -0400 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Jun 24 12:38:49 2011 -0400 @@ -1655,6 +1655,23 @@ _max_live_bytes += g1_note_end.max_live_bytes(); _freed_bytes += g1_note_end.freed_bytes(); + // If we iterate over the global cleanup list at the end of + // cleanup to do this printing we will not guarantee to only + // generate output for the newly-reclaimed regions (the list + // might not be empty at the beginning of cleanup; we might + // still be working on its previous contents). So we do the + // printing here, before we append the new regions to the global + // cleanup list. + + G1HRPrinter* hr_printer = _g1h->hr_printer(); + if (hr_printer->is_active()) { + HeapRegionLinkedListIterator iter(&local_cleanup_list); + while (iter.more_available()) { + HeapRegion* hr = iter.get_next(); + hr_printer->cleanup(hr); + } + } + _cleanup_list->add_as_tail(&local_cleanup_list); assert(local_cleanup_list.is_empty(), "post-condition"); diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Tue Jun 21 15:23:07 2011 -0400 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Jun 24 12:38:49 2011 -0400 @@ -584,12 +584,6 @@ res = _free_list.remove_head_or_null(); } } - if (res != NULL) { - if (G1PrintHeapRegions) { - gclog_or_tty->print_cr("new alloc region "HR_FORMAT, - HR_FORMAT_PARAMS(res)); - } - } return res; } @@ -598,10 +592,15 @@ HeapRegion* alloc_region = NULL; if (_gc_alloc_region_counts[purpose] < g1_policy()->max_regions(purpose)) { alloc_region = new_region(word_size, true /* do_expand */); - if (purpose == GCAllocForSurvived && alloc_region != NULL) { - alloc_region->set_survivor(); + if (alloc_region != NULL) { + if (purpose == GCAllocForSurvived) { + _hr_printer.alloc(alloc_region, G1HRPrinter::Survivor); + alloc_region->set_survivor(); + } else { + _hr_printer.alloc(alloc_region, G1HRPrinter::Old); + } + ++_gc_alloc_region_counts[purpose]; } - ++_gc_alloc_region_counts[purpose]; } else { g1_policy()->note_alloc_region_limit_reached(purpose); } @@ -733,6 +732,17 @@ assert(first_hr->bottom() < new_top && new_top <= first_hr->end(), "new_top should be in this region"); first_hr->set_top(new_top); + if (_hr_printer.is_active()) { + HeapWord* bottom = first_hr->bottom(); + HeapWord* end = first_hr->orig_end(); + if ((first + 1) == last) { + // the series has a single humongous region + _hr_printer.alloc(G1HRPrinter::SingleHumongous, first_hr, new_top); + } else { + // the series has more than one humongous regions + _hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, end); + } + } // Now, we will update the top fields of the "continues humongous" // regions. The reason we need to do this is that, otherwise, @@ -753,10 +763,12 @@ assert(hr->bottom() < new_top && new_top <= hr->end(), "new_top should fall on this region"); hr->set_top(new_top); + _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, new_top); } else { // not last one assert(new_top > hr->end(), "new_top should be above this region"); hr->set_top(hr->end()); + _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, hr->end()); } } // If we have continues humongous regions (hr != NULL), then the @@ -1154,6 +1166,35 @@ } }; +class PostCompactionPrinterClosure: public HeapRegionClosure { +private: + G1HRPrinter* _hr_printer; +public: + bool doHeapRegion(HeapRegion* hr) { + assert(!hr->is_young(), "not expecting to find young regions"); + // We only generate output for non-empty regions. + if (!hr->is_empty()) { + if (!hr->isHumongous()) { + _hr_printer->post_compaction(hr, G1HRPrinter::Old); + } else if (hr->startsHumongous()) { + if (hr->capacity() == (size_t) HeapRegion::GrainBytes) { + // single humongous region + _hr_printer->post_compaction(hr, G1HRPrinter::SingleHumongous); + } else { + _hr_printer->post_compaction(hr, G1HRPrinter::StartsHumongous); + } + } else { + assert(hr->continuesHumongous(), "only way to get here"); + _hr_printer->post_compaction(hr, G1HRPrinter::ContinuesHumongous); + } + } + return false; + } + + PostCompactionPrinterClosure(G1HRPrinter* hr_printer) + : _hr_printer(hr_printer) { } +}; + bool G1CollectedHeap::do_collection(bool explicit_gc, bool clear_all_soft_refs, size_t word_size) { @@ -1235,6 +1276,11 @@ g1_rem_set()->cleanupHRRS(); tear_down_region_lists(); + // We should call this after we retire any currently active alloc + // regions so that all the ALLOC / RETIRE events are generated + // before the start GC event. + _hr_printer.start_gc(true /* full */, (size_t) total_collections()); + // We may have added regions to the current incremental collection // set between the last GC or pause and now. We need to clear the // incremental collection set and then start rebuilding it afresh @@ -1299,6 +1345,17 @@ // Resize the heap if necessary. resize_if_necessary_after_full_collection(explicit_gc ? 0 : word_size); + if (_hr_printer.is_active()) { + // We should do this after we potentially resize the heap so + // that all the COMMIT / UNCOMMIT events are generated before + // the end GC event. + + PostCompactionPrinterClosure cl(hr_printer()); + heap_region_iterate(&cl); + + _hr_printer.end_gc(true /* full */, (size_t) total_collections()); + } + if (_cg1r->use_cache()) { _cg1r->clear_and_record_card_counts(); _cg1r->clear_hot_cache(); @@ -1654,6 +1711,16 @@ update_committed_space(new_end, mr.end()); } _free_list.add_as_tail(&expansion_list); + + if (_hr_printer.is_active()) { + HeapWord* curr = mr.start(); + while (curr < mr.end()) { + HeapWord* curr_end = curr + HeapRegion::GrainWords; + _hr_printer.commit(curr, curr_end); + curr = curr_end; + } + assert(curr == mr.end(), "post-condition"); + } } else { // The expansion of the virtual storage space was unsuccessful. // Let's see if it was because we ran out of swap. @@ -1684,6 +1751,16 @@ HeapWord* old_end = (HeapWord*) _g1_storage.high(); assert(mr.end() == old_end, "post-condition"); if (mr.byte_size() > 0) { + if (_hr_printer.is_active()) { + HeapWord* curr = mr.end(); + while (curr > mr.start()) { + HeapWord* curr_end = curr; + curr -= HeapRegion::GrainWords; + _hr_printer.uncommit(curr, curr_end); + } + assert(curr == mr.start(), "post-condition"); + } + _g1_storage.shrink_by(mr.byte_size()); HeapWord* new_end = (HeapWord*) _g1_storage.high(); assert(mr.start() == new_end, "post-condition"); @@ -1800,6 +1877,10 @@ MutexLocker x(Heap_lock); + // We have to initialize the printer before committing the heap, as + // it will be used then. + _hr_printer.set_active(G1PrintHeapRegions); + // While there are no constraints in the GC code that HeapWordSize // be any particular value, there are multiple other areas in the // system which believe this to be true (e.g. oop->object_size in some @@ -3346,6 +3427,11 @@ // of the collection set!). release_mutator_alloc_region(); + // We should call this after we retire the mutator alloc + // region(s) so that all the ALLOC / RETIRE events are generated + // before the start GC event. + _hr_printer.start_gc(false /* full */, (size_t) total_collections()); + // The elapsed time induced by the start time below deliberately elides // the possible verification above. double start_time_sec = os::elapsedTime(); @@ -3397,6 +3483,22 @@ g1_policy()->choose_collection_set(target_pause_time_ms); + if (_hr_printer.is_active()) { + HeapRegion* hr = g1_policy()->collection_set(); + while (hr != NULL) { + G1HRPrinter::RegionType type; + if (!hr->is_young()) { + type = G1HRPrinter::Old; + } else if (hr->is_survivor()) { + type = G1HRPrinter::Survivor; + } else { + type = G1HRPrinter::Eden; + } + _hr_printer.cset(hr); + hr = hr->next_in_collection_set(); + } + } + // We have chosen the complete collection set. If marking is // active then, we clear the region fields of any of the // concurrent marking tasks whose region fields point into @@ -3517,6 +3619,13 @@ } } } + + // We should do this after we potentially expand the heap so + // that all the COMMIT events are generated before the end GC + // event, and after we retire the GC alloc regions so that all + // RETIRE events are generated before the end GC event. + _hr_printer.end_gc(false /* full */, (size_t) total_collections()); + // We have to do this after we decide whether to expand the heap or not. g1_policy()->print_heap_transition(); @@ -3756,10 +3865,8 @@ } else { // the region was retained from the last collection ++_gc_alloc_region_counts[ap]; - if (G1PrintHeapRegions) { - gclog_or_tty->print_cr("new alloc region "HR_FORMAT, - HR_FORMAT_PARAMS(alloc_region)); - } + + _hr_printer.reuse(alloc_region); } if (alloc_region != NULL) { @@ -4132,11 +4239,7 @@ HeapRegion* r = heap_region_containing(old); if (!r->evacuation_failed()) { r->set_evacuation_failed(true); - if (G1PrintHeapRegions) { - gclog_or_tty->print("overflow in heap region "PTR_FORMAT" " - "["PTR_FORMAT","PTR_FORMAT")\n", - r, r->bottom(), r->end()); - } + _hr_printer.evac_failure(r); } push_on_evac_failure_scan_stack(old); @@ -4197,6 +4300,7 @@ // Now we can do the post-GC stuff on the region. alloc_region->note_end_of_copying(); g1_policy()->record_after_bytes(alloc_region->used()); + _hr_printer.retire(alloc_region); } HeapWord* @@ -5466,12 +5570,14 @@ assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); assert(!force || g1_policy()->can_expand_young_list(), "if force is true we should be able to expand the young list"); - if (force || !g1_policy()->is_young_list_full()) { + bool young_list_full = g1_policy()->is_young_list_full(); + if (force || !young_list_full) { HeapRegion* new_alloc_region = new_region(word_size, false /* do_expand */); if (new_alloc_region != NULL) { 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; } @@ -5486,6 +5592,7 @@ g1_policy()->add_region_to_incremental_cset_lhs(alloc_region); _summary_bytes_used += allocated_bytes; + _hr_printer.retire(alloc_region); } HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size, diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Tue Jun 21 15:23:07 2011 -0400 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Fri Jun 24 12:38:49 2011 -0400 @@ -27,6 +27,7 @@ #include "gc_implementation/g1/concurrentMark.hpp" #include "gc_implementation/g1/g1AllocRegion.hpp" +#include "gc_implementation/g1/g1HRPrinter.hpp" #include "gc_implementation/g1/g1RemSet.hpp" #include "gc_implementation/g1/g1MonitoringSupport.hpp" #include "gc_implementation/g1/heapRegionSeq.hpp" @@ -298,6 +299,8 @@ size_t* _surviving_young_words; + G1HRPrinter _hr_printer; + void setup_surviving_young_words(); void update_surviving_young_words(size_t* surv_young_words); void cleanup_surviving_young_words(); @@ -635,6 +638,8 @@ return _full_collections_completed; } + G1HRPrinter* hr_printer() { return &_hr_printer; } + protected: // Shrink the garbage-first heap by at most the given size (in bytes!). diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Tue Jun 21 15:23:07 2011 -0400 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Fri Jun 24 12:38:49 2011 -0400 @@ -2638,11 +2638,6 @@ assert(_inc_cset_build_state == Active, "Precondition"); assert(!hr->is_young(), "non-incremental add of young region"); - if (G1PrintHeapRegions) { - gclog_or_tty->print_cr("added region to cset "HR_FORMAT, - HR_FORMAT_PARAMS(hr)); - } - if (_g1->mark_in_progress()) _g1->concurrent_mark()->registerCSetRegion(hr); @@ -2808,11 +2803,6 @@ _inc_cset_tail->set_next_in_collection_set(hr); } _inc_cset_tail = hr; - - if (G1PrintHeapRegions) { - gclog_or_tty->print_cr(" added region to incremental cset (RHS) "HR_FORMAT, - HR_FORMAT_PARAMS(hr)); - } } // Add the region to the LHS of the incremental cset @@ -2830,11 +2820,6 @@ _inc_cset_tail = hr; } _inc_cset_head = hr; - - if (G1PrintHeapRegions) { - gclog_or_tty->print_cr(" added region to incremental cset (LHS) "HR_FORMAT, - HR_FORMAT_PARAMS(hr)); - } } #ifndef PRODUCT diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/g1HRPrinter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1HRPrinter.cpp Fri Jun 24 12:38:49 2011 -0400 @@ -0,0 +1,112 @@ +/* + * 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/g1HRPrinter.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#include "utilities/ostream.hpp" + +const char* G1HRPrinter::action_name(ActionType action) { + switch(action) { + case Alloc: return "ALLOC"; + case AllocForce: return "ALLOC-FORCE"; + case Retire: return "RETIRE"; + case Reuse: return "REUSE"; + case CSet: return "CSET"; + case EvacFailure: return "EVAC-FAILURE"; + case Cleanup: return "CLEANUP"; + case PostCompaction: return "POST-COMPACTION"; + case Commit: return "COMMIT"; + case Uncommit: return "UNCOMMIT"; + default: ShouldNotReachHere(); + } + // trying to keep the Windows compiler happy + return NULL; +} + +const char* G1HRPrinter::region_type_name(RegionType type) { + switch (type) { + case Unset: return NULL; + case Eden: return "Eden"; + case Survivor: return "Survivor"; + case Old: return "Old"; + case SingleHumongous: return "SingleH"; + case StartsHumongous: return "StartsH"; + case ContinuesHumongous: return "ContinuesH"; + default: ShouldNotReachHere(); + } + // trying to keep the Windows compiler happy + return NULL; +} + +const char* G1HRPrinter::phase_name(PhaseType phase) { + switch (phase) { + case StartGC: return "StartGC"; + case EndGC: return "EndGC"; + case StartFullGC: return "StartFullGC"; + case EndFullGC: return "EndFullGC"; + default: ShouldNotReachHere(); + } + // trying to keep the Windows compiler happy + return NULL; +} + +#define G1HR_PREFIX " G1HR" + +void G1HRPrinter::print(ActionType action, RegionType type, + HeapRegion* hr, HeapWord* top) { + const char* action_str = action_name(action); + const char* type_str = region_type_name(type); + HeapWord* bottom = hr->bottom(); + + if (type_str != NULL) { + if (top != NULL) { + gclog_or_tty->print_cr(G1HR_PREFIX" %s(%s) "PTR_FORMAT" "PTR_FORMAT, + action_str, type_str, bottom, top); + } else { + gclog_or_tty->print_cr(G1HR_PREFIX" %s(%s) "PTR_FORMAT, + action_str, type_str, bottom); + } + } else { + if (top != NULL) { + gclog_or_tty->print_cr(G1HR_PREFIX" %s "PTR_FORMAT" "PTR_FORMAT, + action_str, bottom, top); + } else { + gclog_or_tty->print_cr(G1HR_PREFIX" %s "PTR_FORMAT, + action_str, bottom); + } + } +} + +void G1HRPrinter::print(ActionType action, HeapWord* bottom, HeapWord* end) { + const char* action_str = action_name(action); + + gclog_or_tty->print_cr(G1HR_PREFIX" %s ["PTR_FORMAT","PTR_FORMAT"]", + action_str, bottom, end); +} + +void G1HRPrinter::print(PhaseType phase, size_t phase_num) { + const char* phase_str = phase_name(phase); + gclog_or_tty->print_cr(G1HR_PREFIX" #%s "SIZE_FORMAT, phase_str, phase_num); +} diff -r e8b0b0392037 -r 5f6f2615433a src/share/vm/gc_implementation/g1/g1HRPrinter.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1HRPrinter.hpp Fri Jun 24 12:38:49 2011 -0400 @@ -0,0 +1,182 @@ +/* + * 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_G1HRPRINTER_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1HRPRINTER_HPP + +#include "memory/allocation.hpp" +#include "gc_implementation/g1/heapRegion.hpp" + +#define SKIP_RETIRED_FULL_REGIONS 1 + +class G1HRPrinter VALUE_OBJ_CLASS_SPEC { +public: + typedef enum { + Alloc, + AllocForce, + Retire, + Reuse, + CSet, + EvacFailure, + Cleanup, + PostCompaction, + Commit, + Uncommit + } ActionType; + + typedef enum { + Unset, + Eden, + Survivor, + Old, + SingleHumongous, + StartsHumongous, + ContinuesHumongous + } RegionType; + + typedef enum { + StartGC, + EndGC, + StartFullGC, + EndFullGC + } PhaseType; + +private: + bool _active; + + static const char* action_name(ActionType action); + static const char* region_type_name(RegionType type); + static const char* phase_name(PhaseType phase); + + // Print an action event. This version is used in most scenarios and + // only prints the region's bottom. The parameters type and top are + // optional (the "not set" values are Unset and NULL). + static void print(ActionType action, RegionType type, + HeapRegion* hr, HeapWord* top); + + // Print an action event. This version prints both the region's + // bottom and end. Used for Commit / Uncommit events. + static void print(ActionType action, HeapWord* bottom, HeapWord* end); + + // Print a phase event. + static void print(PhaseType phase, size_t phase_num); + +public: + // In some places we iterate over a list in order to generate output + // for the list's elements. By exposing this we can avoid this + // iteration if the printer is not active. + const bool is_active() { return _active; } + + // Have to set this explicitly as we have to do this during the + // heap's initialize() method, not in the constructor. + void set_active(bool active) { _active = active; } + + // The methods below are convenient wrappers for the print() methods. + + void alloc(HeapRegion* hr, RegionType type, bool force = false) { + if (is_active()) { + print((!force) ? Alloc : AllocForce, type, hr, NULL); + } + } + + void alloc(RegionType type, HeapRegion* hr, HeapWord* top) { + if (is_active()) { + print(Alloc, type, hr, top); + } + } + + void retire(HeapRegion* hr) { + if (is_active()) { + if (!SKIP_RETIRED_FULL_REGIONS || hr->top() < hr->end()) { + print(Retire, Unset, hr, hr->top()); + } + } + } + + void reuse(HeapRegion* hr) { + if (is_active()) { + print(Reuse, Unset, hr, NULL); + } + } + + void cset(HeapRegion* hr) { + if (is_active()) { + print(CSet, Unset, hr, NULL); + } + } + + void evac_failure(HeapRegion* hr) { + if (is_active()) { + print(EvacFailure, Unset, hr, NULL); + } + } + + void cleanup(HeapRegion* hr) { + if (is_active()) { + print(Cleanup, Unset, hr, NULL); + } + } + + void post_compaction(HeapRegion* hr, RegionType type) { + if (is_active()) { + print(PostCompaction, type, hr, hr->top()); + } + } + + void commit(HeapWord* bottom, HeapWord* end) { + if (is_active()) { + print(Commit, bottom, end); + } + } + + void uncommit(HeapWord* bottom, HeapWord* end) { + if (is_active()) { + print(Uncommit, bottom, end); + } + } + + void start_gc(bool full, size_t gc_num) { + if (is_active()) { + if (!full) { + print(StartGC, gc_num); + } else { + print(StartFullGC, gc_num); + } + } + } + + void end_gc(bool full, size_t gc_num) { + if (is_active()) { + if (!full) { + print(EndGC, gc_num); + } else { + print(EndFullGC, gc_num); + } + } + } + + G1HRPrinter() : _active(false) { } +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1HRPRINTER_HPP