--- old/src/share/vm/gc/g1/g1CollectedHeap.cpp 2015-09-09 16:12:51.671861833 +0200 +++ new/src/share/vm/gc/g1/g1CollectedHeap.cpp 2015-09-09 16:12:51.559861837 +0200 @@ -3992,360 +3992,364 @@ _gc_timer_stw->register_gc_start(); - GCIdMark gc_id_mark; - _gc_tracer_stw->report_gc_start(gc_cause(), _gc_timer_stw->gc_start()); SvcGCMarker sgcm(SvcGCMarker::MINOR); ResourceMark rm; wait_for_root_region_scanning(); - G1Log::update_level(); - print_heap_before_gc(); - trace_heap_before_gc(_gc_tracer_stw); - - verify_region_sets_optional(); - verify_dirty_young_regions(); - - // This call will decide whether this pause is an initial-mark - // pause. If it is, during_initial_mark_pause() will return true - // for the duration of this pause. - g1_policy()->decide_on_conc_mark_initiation(); - - // We do not allow initial-mark to be piggy-backed on a mixed GC. - assert(!collector_state()->during_initial_mark_pause() || - collector_state()->gcs_are_young(), "sanity"); - - // We also do not allow mixed GCs during marking. - assert(!collector_state()->mark_in_progress() || collector_state()->gcs_are_young(), "sanity"); - - // Record whether this pause is an initial mark. When the current - // thread has completed its logging output and it's safe to signal - // the CM thread, the flag's value in the policy has been reset. - bool should_start_conc_mark = collector_state()->during_initial_mark_pause(); - - // Inner scope for scope based logging, timers, and stats collection + bool should_start_conc_mark = fasle; { - EvacuationInfo evacuation_info; + GCIdMark gc_id_mark; + _gc_tracer_stw->report_gc_start(gc_cause(), _gc_timer_stw->gc_start()); - if (collector_state()->during_initial_mark_pause()) { - // We are about to start a marking cycle, so we increment the - // full collection counter. - increment_old_marking_cycles_started(); - register_concurrent_cycle_start(_gc_timer_stw->gc_start()); - } + G1Log::update_level(); + print_heap_before_gc(); + trace_heap_before_gc(_gc_tracer_stw); - _gc_tracer_stw->report_yc_type(collector_state()->yc_type()); + verify_region_sets_optional(); + verify_dirty_young_regions(); - TraceCPUTime tcpu(G1Log::finer(), true, gclog_or_tty); + // This call will decide whether this pause is an initial-mark + // pause. If it is, during_initial_mark_pause() will return true + // for the duration of this pause. + g1_policy()->decide_on_conc_mark_initiation(); + + // We do not allow initial-mark to be piggy-backed on a mixed GC. + assert(!collector_state()->during_initial_mark_pause() || + collector_state()->gcs_are_young(), "sanity"); + + // We also do not allow mixed GCs during marking. + assert(!collector_state()->mark_in_progress() || collector_state()->gcs_are_young(), "sanity"); + + // Record whether this pause is an initial mark. When the current + // thread has completed its logging output and it's safe to signal + // the CM thread, the flag's value in the policy has been reset. + should_start_conc_mark = collector_state()->during_initial_mark_pause(); + + // Inner scope for scope based logging, timers, and stats collection + { + EvacuationInfo evacuation_info; + + if (collector_state()->during_initial_mark_pause()) { + // We are about to start a marking cycle, so we increment the + // full collection counter. + increment_old_marking_cycles_started(); + register_concurrent_cycle_start(_gc_timer_stw->gc_start()); + } - uint active_workers = AdaptiveSizePolicy::calc_active_workers(workers()->total_workers(), - workers()->active_workers(), - Threads::number_of_non_daemon_threads()); - workers()->set_active_workers(active_workers); - - double pause_start_sec = os::elapsedTime(); - g1_policy()->phase_times()->note_gc_start(active_workers, collector_state()->mark_in_progress()); - log_gc_header(); - - TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); - TraceMemoryManagerStats tms(false /* fullGC */, gc_cause()); - - // If the secondary_free_list is not empty, append it to the - // free_list. No need to wait for the cleanup operation to finish; - // the region allocation code will check the secondary_free_list - // and wait if necessary. If the G1StressConcRegionFreeing flag is - // set, skip this step so that the region allocation code has to - // get entries from the secondary_free_list. - if (!G1StressConcRegionFreeing) { - append_secondary_free_list_if_not_empty_with_lock(); - } + _gc_tracer_stw->report_yc_type(collector_state()->yc_type()); - assert(check_young_list_well_formed(), "young list should be well formed"); + TraceCPUTime tcpu(G1Log::finer(), true, gclog_or_tty); - // Don't dynamically change the number of GC threads this early. A value of - // 0 is used to indicate serial work. When parallel work is done, - // it will be set. - - { // Call to jvmpi::post_class_unload_events must occur outside of active GC - IsGCActiveMark x; - - gc_prologue(false); - increment_total_collections(false /* full gc */); - increment_gc_time_stamp(); - - verify_before_gc(); - - check_bitmaps("GC Start"); - - COMPILER2_PRESENT(DerivedPointerTable::clear()); - - // Please see comment in g1CollectedHeap.hpp and - // G1CollectedHeap::ref_processing_init() to see how - // reference processing currently works in G1. - - // Enable discovery in the STW reference processor - ref_processor_stw()->enable_discovery(); - - { - // We want to temporarily turn off discovery by the - // CM ref processor, if necessary, and turn it back on - // on again later if we do. Using a scoped - // NoRefDiscovery object will do this. - NoRefDiscovery no_cm_discovery(ref_processor_cm()); - - // Forget the current alloc region (we might even choose it to be part - // of the collection set!). - _allocator->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()); - - // This timing is only used by the ergonomics to handle our pause target. - // It is unclear why this should not include the full pause. We will - // investigate this in CR 7178365. - // - // Preserving the old comment here if that helps the investigation: - // - // The elapsed time induced by the start time below deliberately elides - // the possible verification above. - double sample_start_time_sec = os::elapsedTime(); + uint active_workers = AdaptiveSizePolicy::calc_active_workers(workers()->total_workers(), + workers()->active_workers(), + Threads::number_of_non_daemon_threads()); + workers()->set_active_workers(active_workers); + + double pause_start_sec = os::elapsedTime(); + g1_policy()->phase_times()->note_gc_start(active_workers, collector_state()->mark_in_progress()); + log_gc_header(); + + TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); + TraceMemoryManagerStats tms(false /* fullGC */, gc_cause()); + + // If the secondary_free_list is not empty, append it to the + // free_list. No need to wait for the cleanup operation to finish; + // the region allocation code will check the secondary_free_list + // and wait if necessary. If the G1StressConcRegionFreeing flag is + // set, skip this step so that the region allocation code has to + // get entries from the secondary_free_list. + if (!G1StressConcRegionFreeing) { + append_secondary_free_list_if_not_empty_with_lock(); + } + + assert(check_young_list_well_formed(), "young list should be well formed"); + + // Don't dynamically change the number of GC threads this early. A value of + // 0 is used to indicate serial work. When parallel work is done, + // it will be set. + + { // Call to jvmpi::post_class_unload_events must occur outside of active GC + IsGCActiveMark x; + + gc_prologue(false); + increment_total_collections(false /* full gc */); + increment_gc_time_stamp(); + + verify_before_gc(); + + check_bitmaps("GC Start"); + + COMPILER2_PRESENT(DerivedPointerTable::clear()); + + // Please see comment in g1CollectedHeap.hpp and + // G1CollectedHeap::ref_processing_init() to see how + // reference processing currently works in G1. + + // Enable discovery in the STW reference processor + ref_processor_stw()->enable_discovery(); + + { + // We want to temporarily turn off discovery by the + // CM ref processor, if necessary, and turn it back on + // on again later if we do. Using a scoped + // NoRefDiscovery object will do this. + NoRefDiscovery no_cm_discovery(ref_processor_cm()); + + // Forget the current alloc region (we might even choose it to be part + // of the collection set!). + _allocator->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()); + + // This timing is only used by the ergonomics to handle our pause target. + // It is unclear why this should not include the full pause. We will + // investigate this in CR 7178365. + // + // Preserving the old comment here if that helps the investigation: + // + // The elapsed time induced by the start time below deliberately elides + // the possible verification above. + double sample_start_time_sec = os::elapsedTime(); #if YOUNG_LIST_VERBOSE - gclog_or_tty->print_cr("\nBefore recording pause start.\nYoung_list:"); - _young_list->print(); - g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); + gclog_or_tty->print_cr("\nBefore recording pause start.\nYoung_list:"); + _young_list->print(); + g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); #endif // YOUNG_LIST_VERBOSE - g1_policy()->record_collection_pause_start(sample_start_time_sec); + g1_policy()->record_collection_pause_start(sample_start_time_sec); #if YOUNG_LIST_VERBOSE - gclog_or_tty->print_cr("\nAfter recording pause start.\nYoung_list:"); - _young_list->print(); + gclog_or_tty->print_cr("\nAfter recording pause start.\nYoung_list:"); + _young_list->print(); #endif // YOUNG_LIST_VERBOSE - if (collector_state()->during_initial_mark_pause()) { - concurrent_mark()->checkpointRootsInitialPre(); - } + if (collector_state()->during_initial_mark_pause()) { + concurrent_mark()->checkpointRootsInitialPre(); + } #if YOUNG_LIST_VERBOSE - gclog_or_tty->print_cr("\nBefore choosing collection set.\nYoung_list:"); - _young_list->print(); - g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); + gclog_or_tty->print_cr("\nBefore choosing collection set.\nYoung_list:"); + _young_list->print(); + g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); #endif // YOUNG_LIST_VERBOSE - g1_policy()->finalize_cset(target_pause_time_ms); + g1_policy()->finalize_cset(target_pause_time_ms); - evacuation_info.set_collectionset_regions(g1_policy()->cset_region_length()); + evacuation_info.set_collectionset_regions(g1_policy()->cset_region_length()); - register_humongous_regions_with_cset(); + register_humongous_regions_with_cset(); - assert(check_cset_fast_test(), "Inconsistency in the InCSetState table."); + assert(check_cset_fast_test(), "Inconsistency in the InCSetState table."); - _cm->note_start_of_gc(); - // We call this after finalize_cset() to - // ensure that the CSet has been finalized. - _cm->verify_no_cset_oops(); - - if (_hr_printer.is_active()) { - HeapRegion* hr = g1_policy()->collection_set(); - while (hr != NULL) { - _hr_printer.cset(hr); - hr = hr->next_in_collection_set(); - } - } + _cm->note_start_of_gc(); + // We call this after finalize_cset() to + // ensure that the CSet has been finalized. + _cm->verify_no_cset_oops(); + + if (_hr_printer.is_active()) { + HeapRegion* hr = g1_policy()->collection_set(); + while (hr != NULL) { + _hr_printer.cset(hr); + hr = hr->next_in_collection_set(); + } + } #ifdef ASSERT - VerifyCSetClosure cl; - collection_set_iterate(&cl); + VerifyCSetClosure cl; + collection_set_iterate(&cl); #endif // ASSERT - setup_surviving_young_words(); + setup_surviving_young_words(); - // Initialize the GC alloc regions. - _allocator->init_gc_alloc_regions(evacuation_info); + // Initialize the GC alloc regions. + _allocator->init_gc_alloc_regions(evacuation_info); - G1ParScanThreadStateSet per_thread_states(this, workers()->active_workers()); - // Actually do the work... - evacuate_collection_set(evacuation_info, &per_thread_states); + G1ParScanThreadStateSet per_thread_states(this, workers()->active_workers()); + // Actually do the work... + evacuate_collection_set(evacuation_info, &per_thread_states); - free_collection_set(g1_policy()->collection_set(), evacuation_info); + free_collection_set(g1_policy()->collection_set(), evacuation_info); - eagerly_reclaim_humongous_regions(); + eagerly_reclaim_humongous_regions(); - g1_policy()->clear_collection_set(); + g1_policy()->clear_collection_set(); - cleanup_surviving_young_words(); + cleanup_surviving_young_words(); - // Start a new incremental collection set for the next pause. - g1_policy()->start_incremental_cset_building(); + // Start a new incremental collection set for the next pause. + g1_policy()->start_incremental_cset_building(); - clear_cset_fast_test(); + clear_cset_fast_test(); - _young_list->reset_sampled_info(); + _young_list->reset_sampled_info(); - // Don't check the whole heap at this point as the - // GC alloc regions from this pause have been tagged - // as survivors and moved on to the survivor list. - // Survivor regions will fail the !is_young() check. - assert(check_young_list_empty(false /* check_heap */), - "young list should be empty"); + // Don't check the whole heap at this point as the + // GC alloc regions from this pause have been tagged + // as survivors and moved on to the survivor list. + // Survivor regions will fail the !is_young() check. + assert(check_young_list_empty(false /* check_heap */), + "young list should be empty"); #if YOUNG_LIST_VERBOSE - gclog_or_tty->print_cr("Before recording survivors.\nYoung List:"); - _young_list->print(); + gclog_or_tty->print_cr("Before recording survivors.\nYoung List:"); + _young_list->print(); #endif // YOUNG_LIST_VERBOSE - g1_policy()->record_survivor_regions(_young_list->survivor_length(), - _young_list->first_survivor_region(), - _young_list->last_survivor_region()); - - _young_list->reset_auxilary_lists(); - - if (evacuation_failed()) { - set_used(recalculate_used()); - if (_archive_allocator != NULL) { - _archive_allocator->clear_used(); - } - for (uint i = 0; i < ParallelGCThreads; i++) { - if (_evacuation_failed_info_array[i].has_failed()) { - _gc_tracer_stw->report_evacuation_failed(_evacuation_failed_info_array[i]); - } - } - } else { - // The "used" of the the collection set have already been subtracted - // when they were freed. Add in the bytes evacuated. - increase_used(g1_policy()->bytes_copied_during_gc()); - } - - if (collector_state()->during_initial_mark_pause()) { - // We have to do this before we notify the CM threads that - // they can start working to make sure that all the - // appropriate initialization is done on the CM object. - concurrent_mark()->checkpointRootsInitialPost(); - collector_state()->set_mark_in_progress(true); - // Note that we don't actually trigger the CM thread at - // this point. We do that later when we're sure that - // the current thread has completed its logging output. - } + g1_policy()->record_survivor_regions(_young_list->survivor_length(), + _young_list->first_survivor_region(), + _young_list->last_survivor_region()); + + _young_list->reset_auxilary_lists(); + + if (evacuation_failed()) { + set_used(recalculate_used()); + if (_archive_allocator != NULL) { + _archive_allocator->clear_used(); + } + for (uint i = 0; i < ParallelGCThreads; i++) { + if (_evacuation_failed_info_array[i].has_failed()) { + _gc_tracer_stw->report_evacuation_failed(_evacuation_failed_info_array[i]); + } + } + } else { + // The "used" of the the collection set have already been subtracted + // when they were freed. Add in the bytes evacuated. + increase_used(g1_policy()->bytes_copied_during_gc()); + } + + if (collector_state()->during_initial_mark_pause()) { + // We have to do this before we notify the CM threads that + // they can start working to make sure that all the + // appropriate initialization is done on the CM object. + concurrent_mark()->checkpointRootsInitialPost(); + collector_state()->set_mark_in_progress(true); + // Note that we don't actually trigger the CM thread at + // this point. We do that later when we're sure that + // the current thread has completed its logging output. + } - allocate_dummy_regions(); + allocate_dummy_regions(); #if YOUNG_LIST_VERBOSE - gclog_or_tty->print_cr("\nEnd of the pause.\nYoung_list:"); - _young_list->print(); - g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); + gclog_or_tty->print_cr("\nEnd of the pause.\nYoung_list:"); + _young_list->print(); + g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); #endif // YOUNG_LIST_VERBOSE - _allocator->init_mutator_alloc_region(); - - { - 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. Cannot do anything about it. - } - } - } - - // We redo the verification but now wrt to the new CSet which - // has just got initialized after the previous CSet was freed. - _cm->verify_no_cset_oops(); - _cm->note_end_of_gc(); - - // This timing is only used by the ergonomics to handle our pause target. - // It is unclear why this should not include the full pause. We will - // investigate this in CR 7178365. - double sample_end_time_sec = os::elapsedTime(); - double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS; - g1_policy()->record_collection_pause_end(pause_time_ms); - - evacuation_info.set_collectionset_used_before(g1_policy()->collection_set_bytes_used_before()); - evacuation_info.set_bytes_copied(g1_policy()->bytes_copied_during_gc()); - - MemoryService::track_memory_usage(); - - // In prepare_for_verify() below we'll need to scan the deferred - // update buffers to bring the RSets up-to-date if - // G1HRRSFlushLogBuffersOnVerify has been set. While scanning - // the update buffers we'll probably need to scan cards on the - // regions we just allocated to (i.e., the GC alloc - // regions). However, during the last GC we called - // set_saved_mark() on all the GC alloc regions, so card - // scanning might skip the [saved_mark_word()...top()] area of - // those regions (i.e., the area we allocated objects into - // during the last GC). But it shouldn't. Given that - // saved_mark_word() is conditional on whether the GC time stamp - // on the region is current or not, by incrementing the GC time - // stamp here we invalidate all the GC time stamps on all the - // regions and saved_mark_word() will simply return top() for - // all the regions. This is a nicer way of ensuring this rather - // than iterating over the regions and fixing them. In fact, the - // GC time stamp increment here also ensures that - // saved_mark_word() will return top() between pauses, i.e., - // during concurrent refinement. So we don't need the - // is_gc_active() check to decided which top to use when - // scanning cards (see CR 7039627). - increment_gc_time_stamp(); + _allocator->init_mutator_alloc_region(); - verify_after_gc(); - check_bitmaps("GC End"); - - assert(!ref_processor_stw()->discovery_enabled(), "Postcondition"); - ref_processor_stw()->verify_no_references_recorded(); - - // CM reference discovery will be re-enabled if necessary. - } - - // 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()); + { + 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. Cannot do anything about it. + } + } + } + + // We redo the verification but now wrt to the new CSet which + // has just got initialized after the previous CSet was freed. + _cm->verify_no_cset_oops(); + _cm->note_end_of_gc(); + + // This timing is only used by the ergonomics to handle our pause target. + // It is unclear why this should not include the full pause. We will + // investigate this in CR 7178365. + double sample_end_time_sec = os::elapsedTime(); + double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS; + g1_policy()->record_collection_pause_end(pause_time_ms); + + evacuation_info.set_collectionset_used_before(g1_policy()->collection_set_bytes_used_before()); + evacuation_info.set_bytes_copied(g1_policy()->bytes_copied_during_gc()); + + MemoryService::track_memory_usage(); + + // In prepare_for_verify() below we'll need to scan the deferred + // update buffers to bring the RSets up-to-date if + // G1HRRSFlushLogBuffersOnVerify has been set. While scanning + // the update buffers we'll probably need to scan cards on the + // regions we just allocated to (i.e., the GC alloc + // regions). However, during the last GC we called + // set_saved_mark() on all the GC alloc regions, so card + // scanning might skip the [saved_mark_word()...top()] area of + // those regions (i.e., the area we allocated objects into + // during the last GC). But it shouldn't. Given that + // saved_mark_word() is conditional on whether the GC time stamp + // on the region is current or not, by incrementing the GC time + // stamp here we invalidate all the GC time stamps on all the + // regions and saved_mark_word() will simply return top() for + // all the regions. This is a nicer way of ensuring this rather + // than iterating over the regions and fixing them. In fact, the + // GC time stamp increment here also ensures that + // saved_mark_word() will return top() between pauses, i.e., + // during concurrent refinement. So we don't need the + // is_gc_active() check to decided which top to use when + // scanning cards (see CR 7039627). + increment_gc_time_stamp(); + + verify_after_gc(); + check_bitmaps("GC End"); + + assert(!ref_processor_stw()->discovery_enabled(), "Postcondition"); + ref_processor_stw()->verify_no_references_recorded(); + + // CM reference discovery will be re-enabled if necessary. + } + + // 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()); #ifdef TRACESPINNING - ParallelTaskTerminator::print_termination_counts(); + ParallelTaskTerminator::print_termination_counts(); #endif - gc_epilogue(false); - } - - // Print the remainder of the GC log output. - log_gc_footer(os::elapsedTime() - pause_start_sec); - - // It is not yet to safe to tell the concurrent mark to - // start as we have some optional output below. We don't want the - // output from the concurrent mark thread interfering with this - // logging output either. - - _hrm.verify_optional(); - verify_region_sets_optional(); - - TASKQUEUE_STATS_ONLY(if (PrintTaskqueue) print_taskqueue_stats()); - TASKQUEUE_STATS_ONLY(reset_taskqueue_stats()); + gc_epilogue(false); + } - print_heap_after_gc(); - trace_heap_after_gc(_gc_tracer_stw); + // Print the remainder of the GC log output. + log_gc_footer(os::elapsedTime() - pause_start_sec); - // We must call G1MonitoringSupport::update_sizes() in the same scoping level - // as an active TraceMemoryManagerStats object (i.e. before the destructor for the - // TraceMemoryManagerStats is called) so that the G1 memory pools are updated - // before any GC notifications are raised. - g1mm()->update_sizes(); - - _gc_tracer_stw->report_evacuation_info(&evacuation_info); - _gc_tracer_stw->report_tenuring_threshold(_g1_policy->tenuring_threshold()); - _gc_timer_stw->register_gc_end(); - _gc_tracer_stw->report_gc_end(_gc_timer_stw->gc_end(), _gc_timer_stw->time_partitions()); + // It is not yet to safe to tell the concurrent mark to + // start as we have some optional output below. We don't want the + // output from the concurrent mark thread interfering with this + // logging output either. + + _hrm.verify_optional(); + verify_region_sets_optional(); + + TASKQUEUE_STATS_ONLY(if (PrintTaskqueue) print_taskqueue_stats()); + TASKQUEUE_STATS_ONLY(reset_taskqueue_stats()); + + print_heap_after_gc(); + trace_heap_after_gc(_gc_tracer_stw); + + // We must call G1MonitoringSupport::update_sizes() in the same scoping level + // as an active TraceMemoryManagerStats object (i.e. before the destructor for the + // TraceMemoryManagerStats is called) so that the G1 memory pools are updated + // before any GC notifications are raised. + g1mm()->update_sizes(); + + _gc_tracer_stw->report_evacuation_info(&evacuation_info); + _gc_tracer_stw->report_tenuring_threshold(_g1_policy->tenuring_threshold()); + _gc_timer_stw->register_gc_end(); + _gc_tracer_stw->report_gc_end(_gc_timer_stw->gc_end(), _gc_timer_stw->time_partitions()); + } + // It should now be safe to tell the concurrent mark thread to start + // without its logging output interfering with the logging output + // that came from the pause. } - // It should now be safe to tell the concurrent mark thread to start - // without its logging output interfering with the logging output - // that came from the pause. if (should_start_conc_mark) { // CAUTION: after the doConcurrentMark() call below,