Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openjdk/src/hotspot/share/gc/g1/   (Sun/Oracle ©)  Datei vom 4.0.2023 mit Größe 112 kB image not shown  

Quelle  g1ConcurrentMark.cpp   Sprache: C

 
/*
 * Copyright (c) 2001, 2022, 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 "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BatchedTask.hpp"
#include "gc/g1/g1CardSetMemory.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectorState.hpp"
#include "gc/g1/g1ConcurrentMark.inline.hpp"
#include "gc/g1/g1ConcurrentMarkThread.inline.hpp"
#include "gc/g1/g1ConcurrentRebuildAndScrub.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/g1/g1HeapVerifier.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/g1RegionMarkStatsCache.inline.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
#include "gc/g1/g1Trace.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/g1/heapRegionRemSet.inline.hpp"
#include "gc/g1/heapRegionSet.inline.hpp"
#include "gc/shared/gcId.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/gcVMOperations.hpp"
#include "gc/shared/genOopClosures.inline.hpp"
#include "gc/shared/referencePolicy.hpp"
#include "gc/shared/strongRootsScope.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/taskTerminator.hpp"
#include "gc/shared/taskqueue.inline.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "gc/shared/workerPolicy.hpp"
#include "jvm.h"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "memory/iterator.hpp"
#include "memory/metaspaceUtils.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/prefetch.inline.hpp"
#include "runtime/threads.hpp"
#include "services/memTracker.hpp"
#include "utilities/align.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/growableArray.hpp"

bool G1CMBitMapClosure::do_addr(HeapWord* const addr) {
  assert(addr < _cm->finger(), "invariant");
  assert(addr >= _task->finger(), "invariant");

  // We move that task's local finger along.
  _task->move_finger_to(addr);

  _task->scan_task_entry(G1TaskQueueEntry::from_oop(cast_to_oop(addr)));
  // we only partially drain the local queue and global stack
  _task->drain_local_queue(true);
  _task->drain_global_stack(true);

  // if the has_aborted flag has been raised, we need to bail out of
  // the iteration
  return !_task->has_aborted();
}

G1CMMarkStack::G1CMMarkStack() :
  _max_chunk_capacity(0),
  _base(NULL),
  _chunk_capacity(0) {
  set_empty();
}

bool G1CMMarkStack::resize(size_t new_capacity) {
  assert(is_empty(), "Only resize when stack is empty.");
  assert(new_capacity <= _max_chunk_capacity,
         "Trying to resize stack to " SIZE_FORMAT " chunks when the maximum is " SIZE_FORMAT, new_capacity, _max_chunk_capacity);

  TaskQueueEntryChunk* new_base = MmapArrayAllocator<TaskQueueEntryChunk>::allocate_or_null(new_capacity, mtGC);

  if (new_base == NULL) {
    log_warning(gc)("Failed to reserve memory for new overflow mark stack with " SIZE_FORMAT " chunks and size " SIZE_FORMAT "B.", new_capacity, new_capacity * sizeof(TaskQueueEntryChunk));
    return false;
  }
  // Release old mapping.
  if (_base != NULL) {
    MmapArrayAllocator<TaskQueueEntryChunk>::free(_base, _chunk_capacity);
  }

  _base = new_base;
  _chunk_capacity = new_capacity;
  set_empty();

  return true;
}

size_t G1CMMarkStack::capacity_alignment() {
  return (size_t)lcm(os::vm_allocation_granularity(), sizeof(TaskQueueEntryChunk)) / sizeof(G1TaskQueueEntry);
}

bool G1CMMarkStack::initialize(size_t initial_capacity, size_t max_capacity) {
  guarantee(_max_chunk_capacity == 0, "G1CMMarkStack already initialized.");

  size_t const TaskEntryChunkSizeInVoidStar = sizeof(TaskQueueEntryChunk) / sizeof(G1TaskQueueEntry);

  _max_chunk_capacity = align_up(max_capacity, capacity_alignment()) / TaskEntryChunkSizeInVoidStar;
  size_t initial_chunk_capacity = align_up(initial_capacity, capacity_alignment()) / TaskEntryChunkSizeInVoidStar;

  guarantee(initial_chunk_capacity <= _max_chunk_capacity,
            "Maximum chunk capacity " SIZE_FORMAT " smaller than initial capacity " SIZE_FORMAT,
            _max_chunk_capacity,
            initial_chunk_capacity);

  log_debug(gc)("Initialize mark stack with " SIZE_FORMAT " chunks, maximum " SIZE_FORMAT,
                initial_chunk_capacity, _max_chunk_capacity);

  return resize(initial_chunk_capacity);
}

void G1CMMarkStack::expand() {
  if (_chunk_capacity == _max_chunk_capacity) {
    log_debug(gc)("Can not expand overflow mark stack further, already at maximum capacity of " SIZE_FORMAT " chunks.", _chunk_capacity);
    return;
  }
  size_t old_capacity = _chunk_capacity;
  // Double capacity if possible
  size_t new_capacity = MIN2(old_capacity * 2, _max_chunk_capacity);

  if (resize(new_capacity)) {
    log_debug(gc)("Expanded mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks",
                  old_capacity, new_capacity);
  } else {
    log_warning(gc)("Failed to expand mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks",
                    old_capacity, new_capacity);
  }
}

G1CMMarkStack::~G1CMMarkStack() {
  if (_base != NULL) {
    MmapArrayAllocator<TaskQueueEntryChunk>::free(_base, _chunk_capacity);
  }
}

void G1CMMarkStack::add_chunk_to_list(TaskQueueEntryChunk* volatile* list, TaskQueueEntryChunk* elem) {
  elem->next = *list;
  *list = elem;
}

void G1CMMarkStack::add_chunk_to_chunk_list(TaskQueueEntryChunk* elem) {
  MutexLocker x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag);
  add_chunk_to_list(&_chunk_list, elem);
  _chunks_in_chunk_list++;
}

void G1CMMarkStack::add_chunk_to_free_list(TaskQueueEntryChunk* elem) {
  MutexLocker x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag);
  add_chunk_to_list(&_free_list, elem);
}

G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_list(TaskQueueEntryChunk* volatile* list) {
  TaskQueueEntryChunk* result = *list;
  if (result != NULL) {
    *list = (*list)->next;
  }
  return result;
}

G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_chunk_list() {
  MutexLocker x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag);
  TaskQueueEntryChunk* result = remove_chunk_from_list(&_chunk_list);
  if (result != NULL) {
    _chunks_in_chunk_list--;
  }
  return result;
}

G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_free_list() {
  MutexLocker x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag);
  return remove_chunk_from_list(&_free_list);
}

G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::allocate_new_chunk() {
  // This dirty read of _hwm is okay because we only ever increase the _hwm in parallel code.
  // Further this limits _hwm to a value of _chunk_capacity + #threads, avoiding
  // wraparound of _hwm.
  if (_hwm >= _chunk_capacity) {
    return NULL;
  }

  size_t cur_idx = Atomic::fetch_and_add(&_hwm, 1u);
  if (cur_idx >= _chunk_capacity) {
    return NULL;
  }

  TaskQueueEntryChunk* result = ::new (&_base[cur_idx]) TaskQueueEntryChunk;
  result->next = NULL;
  return result;
}

bool G1CMMarkStack::par_push_chunk(G1TaskQueueEntry* ptr_arr) {
  // Get a new chunk.
  TaskQueueEntryChunk* new_chunk = remove_chunk_from_free_list();

  if (new_chunk == NULL) {
    // Did not get a chunk from the free list. Allocate from backing memory.
    new_chunk = allocate_new_chunk();

    if (new_chunk == NULL) {
      return false;
    }
  }

  Copy::conjoint_memory_atomic(ptr_arr, new_chunk->data, EntriesPerChunk * sizeof(G1TaskQueueEntry));

  add_chunk_to_chunk_list(new_chunk);

  return true;
}

bool G1CMMarkStack::par_pop_chunk(G1TaskQueueEntry* ptr_arr) {
  TaskQueueEntryChunk* cur = remove_chunk_from_chunk_list();

  if (cur == NULL) {
    return false;
  }

  Copy::conjoint_memory_atomic(cur->data, ptr_arr, EntriesPerChunk * sizeof(G1TaskQueueEntry));

  add_chunk_to_free_list(cur);
  return true;
}

void G1CMMarkStack::set_empty() {
  _chunks_in_chunk_list = 0;
  _hwm = 0;
  _chunk_list = NULL;
  _free_list = NULL;
}

G1CMRootMemRegions::G1CMRootMemRegions(uint const max_regions) :
    _root_regions(MemRegion::create_array(max_regions, mtGC)),
    _max_regions(max_regions),
    _num_root_regions(0),
    _claimed_root_regions(0),
    _scan_in_progress(false),
    _should_abort(false) { }

G1CMRootMemRegions::~G1CMRootMemRegions() {
  MemRegion::destroy_array(_root_regions, _max_regions);
}

void G1CMRootMemRegions::reset() {
  _num_root_regions = 0;
}

void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) {
  assert_at_safepoint();
  size_t idx = Atomic::fetch_and_add(&_num_root_regions, 1u);
  assert(idx < _max_regions, "Trying to add more root MemRegions than there is space " SIZE_FORMAT, _max_regions);
  assert(start != NULL && end != NULL && start <= end, "Start (" PTR_FORMAT ") should be less or equal to "
         "end (" PTR_FORMAT ")", p2i(start), p2i(end));
  _root_regions[idx].set_start(start);
  _root_regions[idx].set_end(end);
}

void G1CMRootMemRegions::prepare_for_scan() {
  assert(!scan_in_progress(), "pre-condition");

  _scan_in_progress = _num_root_regions > 0;

  _claimed_root_regions = 0;
  _should_abort = false;
}

const MemRegion* G1CMRootMemRegions::claim_next() {
  if (_should_abort) {
    // If someone has set the should_abort flag, we return NULL to
    // force the caller to bail out of their loop.
    return NULL;
  }

  if (_claimed_root_regions >= _num_root_regions) {
    return NULL;
  }

  size_t claimed_index = Atomic::fetch_and_add(&_claimed_root_regions, 1u);
  if (claimed_index < _num_root_regions) {
    return &_root_regions[claimed_index];
  }
  return NULL;
}

uint G1CMRootMemRegions::num_root_regions() const {
  return (uint)_num_root_regions;
}

void G1CMRootMemRegions::notify_scan_done() {
  MutexLocker x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag);
  _scan_in_progress = false;
  RootRegionScan_lock->notify_all();
}

void G1CMRootMemRegions::cancel_scan() {
  notify_scan_done();
}

void G1CMRootMemRegions::scan_finished() {
  assert(scan_in_progress(), "pre-condition");

  if (!_should_abort) {
    assert(_claimed_root_regions >= num_root_regions(),
           "we should have claimed all root regions, claimed " SIZE_FORMAT ", length = %u",
           _claimed_root_regions, num_root_regions());
  }

  notify_scan_done();
}

bool G1CMRootMemRegions::wait_until_scan_finished() {
  if (!scan_in_progress()) {
    return false;
  }

  {
    MonitorLocker ml(RootRegionScan_lock, Mutex::_no_safepoint_check_flag);
    while (scan_in_progress()) {
      ml.wait();
    }
  }
  return true;
}

G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
                                   G1RegionToSpaceMapper* bitmap_storage) :
  // _cm_thread set inside the constructor
  _g1h(g1h),

  _mark_bitmap(),

  _heap(_g1h->reserved()),

  _root_regions(_g1h->max_regions()),

  _global_mark_stack(),

  // _finger set in set_non_marking_state

  _worker_id_offset(G1DirtyCardQueueSet::num_par_ids() + G1ConcRefinementThreads),
  _max_num_tasks(MAX2(ConcGCThreads, ParallelGCThreads)),
  // _num_active_tasks set in set_non_marking_state()
  // _tasks set inside the constructor

  _task_queues(new G1CMTaskQueueSet((int) _max_num_tasks)),
  _terminator((int) _max_num_tasks, _task_queues),

  _first_overflow_barrier_sync(),
  _second_overflow_barrier_sync(),

  _completed_mark_cycles(0),
  _has_overflown(false),
  _concurrent(false),
  _has_aborted(false),
  _restart_for_overflow(false),
  _gc_timer_cm(new ConcurrentGCTimer()),
  _gc_tracer_cm(new G1OldTracer()),

  // _verbose_level set below

  _init_times(),
  _remark_times(),
  _remark_mark_times(),
  _remark_weak_ref_times(),
  _cleanup_times(),
  _total_cleanup_time(0.0),

  _accum_task_vtime(NULL),

  _concurrent_workers(NULL),
  _num_concurrent_workers(0),
  _max_concurrent_workers(0),

  _region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_reserved_regions(), mtGC)),
  _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_reserved_regions(), mtGC)),
  _needs_remembered_set_rebuild(false)
{
  assert(CGC_lock != NULL, "CGC_lock must be initialized");

  _mark_bitmap.initialize(g1h->reserved(), bitmap_storage);

  // Create & start ConcurrentMark thread.
  _cm_thread = new G1ConcurrentMarkThread(this);
  if (_cm_thread->osthread() == NULL) {
    vm_shutdown_during_initialization("Could not create ConcurrentMarkThread");
  }

  log_debug(gc)("ConcGCThreads: %u offset %u", ConcGCThreads, _worker_id_offset);
  log_debug(gc)("ParallelGCThreads: %u", ParallelGCThreads);

  _num_concurrent_workers = ConcGCThreads;
  _max_concurrent_workers = _num_concurrent_workers;

  _concurrent_workers = new WorkerThreads("G1 Conc", _max_concurrent_workers);
  _concurrent_workers->initialize_workers();

  if (!_global_mark_stack.initialize(MarkStackSize, MarkStackSizeMax)) {
    vm_exit_during_initialization("Failed to allocate initial concurrent mark overflow mark stack.");
  }

  _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_num_tasks, mtGC);
  _accum_task_vtime = NEW_C_HEAP_ARRAY(double, _max_num_tasks, mtGC);

  // so that the assertion in MarkingTaskQueue::task_queue doesn't fail
  _num_active_tasks = _max_num_tasks;

  for (uint i = 0; i < _max_num_tasks; ++i) {
    G1CMTaskQueue* task_queue = new G1CMTaskQueue();
    _task_queues->register_queue(i, task_queue);

    _tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats);

    _accum_task_vtime[i] = 0.0;
  }

  reset_at_marking_complete();
}

void G1ConcurrentMark::reset() {
  _has_aborted = false;

  reset_marking_for_restart();

  // Reset all tasks, since different phases will use different number of active
  // threads. So, it's easiest to have all of them ready.
  for (uint i = 0; i < _max_num_tasks; ++i) {
    _tasks[i]->reset(mark_bitmap());
  }

  uint max_reserved_regions = _g1h->max_reserved_regions();
  for (uint i = 0; i < max_reserved_regions; i++) {
    _top_at_rebuild_starts[i] = NULL;
    _region_mark_stats[i].clear();
  }

  _root_regions.reset();
}

void G1ConcurrentMark::clear_statistics_in_region(uint region_idx) {
  for (uint j = 0; j < _max_num_tasks; ++j) {
    _tasks[j]->clear_mark_stats_cache(region_idx);
  }
  _top_at_rebuild_starts[region_idx] = NULL;
  _region_mark_stats[region_idx].clear();
}

void G1ConcurrentMark::clear_statistics(HeapRegion* r) {
  uint const region_idx = r->hrm_index();
  if (r->is_humongous()) {
    assert(r->is_starts_humongous(), "Got humongous continues region here");
    uint const size_in_regions = (uint)_g1h->humongous_obj_size_in_regions(cast_to_oop(r->humongous_start_region()->bottom())->size());
    for (uint j = region_idx; j < (region_idx + size_in_regions); j++) {
      clear_statistics_in_region(j);
    }
  } else {
    clear_statistics_in_region(region_idx);
  }
}

void G1ConcurrentMark::humongous_object_eagerly_reclaimed(HeapRegion* r) {
  assert_at_safepoint();

  // Need to clear mark bit of the humongous object. Doing this unconditionally is fine.
  mark_bitmap()->clear(r->bottom());

  if (!_g1h->collector_state()->mark_or_rebuild_in_progress()) {
    return;
  }

  // Clear any statistics about the region gathered so far.
  clear_statistics(r);
}

void G1ConcurrentMark::reset_marking_for_restart() {
  _global_mark_stack.set_empty();

  // Expand the marking stack, if we have to and if we can.
  if (has_overflown()) {
    _global_mark_stack.expand();

    uint max_reserved_regions = _g1h->max_reserved_regions();
    for (uint i = 0; i < max_reserved_regions; i++) {
      _region_mark_stats[i].clear_during_overflow();
    }
  }

  clear_has_overflown();
  _finger = _heap.start();

  for (uint i = 0; i < _max_num_tasks; ++i) {
    G1CMTaskQueue* queue = _task_queues->queue(i);
    queue->set_empty();
  }
}

void G1ConcurrentMark::set_concurrency(uint active_tasks) {
  assert(active_tasks <= _max_num_tasks, "we should not have more");

  _num_active_tasks = active_tasks;
  // Need to update the three data structures below according to the
  // number of active threads for this phase.
  _terminator.reset_for_reuse(active_tasks);
  _first_overflow_barrier_sync.set_n_workers((int) active_tasks);
  _second_overflow_barrier_sync.set_n_workers((int) active_tasks);
}

void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) {
  set_concurrency(active_tasks);

  _concurrent = concurrent;

  if (!concurrent) {
    // At this point we should be in a STW phase, and completed marking.
    assert_at_safepoint_on_vm_thread();
    assert(out_of_regions(),
           "only way to get here: _finger: " PTR_FORMAT ", _heap_end: " PTR_FORMAT,
           p2i(_finger), p2i(_heap.end()));
  }
}

void G1ConcurrentMark::reset_at_marking_complete() {
  // We set the global marking state to some default values when we're
  // not doing marking.
  reset_marking_for_restart();
  _num_active_tasks = 0;
}

G1ConcurrentMark::~G1ConcurrentMark() {
  FREE_C_HEAP_ARRAY(HeapWord*, _top_at_rebuild_starts);
  FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats);
  // The G1ConcurrentMark instance is never freed.
  ShouldNotReachHere();
}

class G1ClearBitMapTask : public WorkerTask {
public:
  static size_t chunk_size() { return M; }

private:
  // Heap region closure used for clearing the _mark_bitmap.
  class G1ClearBitmapHRClosure : public HeapRegionClosure {
  private:
    G1ConcurrentMark* _cm;
    G1CMBitMap* _bitmap;
    bool _suspendible; // If suspendible, do yield checks.

    bool suspendible() {
      return _suspendible;
    }

    bool is_clear_concurrent_undo() {
      return suspendible() && _cm->cm_thread()->in_undo_mark();
    }

    bool has_aborted() {
      if (suspendible()) {
        _cm->do_yield_check();
        return _cm->has_aborted();
      }
      return false;
    }

    HeapWord* region_clear_limit(HeapRegion* r) {
      // During a Concurrent Undo Mark cycle, the per region top_at_mark_start and
      // live_words data are current wrt to the _mark_bitmap. We use this information
      // to only clear ranges of the bitmap that require clearing.
      if (is_clear_concurrent_undo()) {
        // No need to clear bitmaps for empty regions (which includes regions we
        // did not mark through).
        if (_cm->live_words(r->hrm_index()) == 0) {
          assert(_bitmap->get_next_marked_addr(r->bottom(), r->end()) == r->end(), "Should not have marked bits");
          return r->bottom();
        }
        assert(_bitmap->get_next_marked_addr(r->top_at_mark_start(), r->end()) == r->end(), "Should not have marked bits above tams");
      }
      return r->end();
    }

  public:
    G1ClearBitmapHRClosure(G1ConcurrentMark* cm, bool suspendible) :
      HeapRegionClosure(),
      _cm(cm),
      _bitmap(cm->mark_bitmap()),
      _suspendible(suspendible)
    { }

    virtual bool do_heap_region(HeapRegion* r) {
      if (has_aborted()) {
        return true;
      }

      HeapWord* cur = r->bottom();
      HeapWord* const end = region_clear_limit(r);

      size_t const chunk_size_in_words = G1ClearBitMapTask::chunk_size() / HeapWordSize;

      while (cur < end) {

        MemRegion mr(cur, MIN2(cur + chunk_size_in_words, end));
        _bitmap->clear_range(mr);

        cur += chunk_size_in_words;

        // Repeat the asserts from before the start of the closure. We will do them
        // as asserts here to minimize their overhead on the product. However, we
        // will have them as guarantees at the beginning / end of the bitmap
        // clearing to get some checking in the product.
        assert(!suspendible() || _cm->cm_thread()->in_progress(), "invariant");
        assert(!suspendible() || !G1CollectedHeap::heap()->collector_state()->mark_or_rebuild_in_progress(), "invariant");

        // Abort iteration if necessary.
        if (has_aborted()) {
          return true;
        }
      }
      assert(cur >= end, "Must have completed iteration over the bitmap for region %u.", r->hrm_index());

      r->reset_top_at_mark_start();

      return false;
    }
  };

  G1ClearBitmapHRClosure _cl;
  HeapRegionClaimer _hr_claimer;
  bool _suspendible; // If the task is suspendible, workers must join the STS.

public:
  G1ClearBitMapTask(G1ConcurrentMark* cm, uint n_workers, bool suspendible) :
    WorkerTask("G1 Clear Bitmap"),
    _cl(cm, suspendible),
    _hr_claimer(n_workers),
    _suspendible(suspendible)
  { }

  void work(uint worker_id) {
    SuspendibleThreadSetJoiner sts_join(_suspendible);
    G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&_cl, &_hr_claimer, worker_id);
  }

  bool is_complete() {
    return _cl.is_complete();
  }
};

void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) {
  assert(may_yield || SafepointSynchronize::is_at_safepoint(), "Non-yielding bitmap clear only allowed at safepoint.");

  size_t const num_bytes_to_clear = (HeapRegion::GrainBytes * _g1h->num_regions()) / G1CMBitMap::heap_map_factor();
  size_t const num_chunks = align_up(num_bytes_to_clear, G1ClearBitMapTask::chunk_size()) / G1ClearBitMapTask::chunk_size();

  uint const num_workers = (uint)MIN2(num_chunks, (size_t)workers->active_workers());

  G1ClearBitMapTask cl(this, num_workers, may_yield);

  log_debug(gc, ergo)("Running %s with %u workers for " SIZE_FORMAT " work units.", cl.name(), num_workers, num_chunks);
  workers->run_task(&cl, num_workers);
  guarantee(may_yield || cl.is_complete(), "Must have completed iteration when not yielding.");
}

void G1ConcurrentMark::cleanup_for_next_mark() {
  // Make sure that the concurrent mark thread looks to still be in
  // the current cycle.
  guarantee(cm_thread()->in_progress(), "invariant");

  // We are finishing up the current cycle by clearing the next
  // marking bitmap and getting it ready for the next cycle. During
  // this time no other cycle can start. So, let's make sure that this
  // is the case.
  guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant");

  clear_bitmap(_concurrent_workers, true);

  // Repeat the asserts from above.
  guarantee(cm_thread()->in_progress(), "invariant");
  guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant");
}

void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers) {
  assert_at_safepoint_on_vm_thread();
  // To avoid fragmentation the full collection requesting to clear the bitmap
  // might use fewer workers than available. To ensure the bitmap is cleared
  // as efficiently as possible the number of active workers are temporarily
  // increased to include all currently created workers.
  WithActiveWorkers update(workers, workers->created_workers());
  clear_bitmap(workers, false);
}

class G1PreConcurrentStartTask : public G1BatchedTask {
  // Reset marking state.
  class ResetMarkingStateTask;
  // For each region note start of marking.
  class NoteStartOfMarkTask;

public:
  G1PreConcurrentStartTask(GCCause::Cause cause, G1ConcurrentMark* cm);
};

class G1PreConcurrentStartTask::ResetMarkingStateTask : public G1AbstractSubTask {
  G1ConcurrentMark* _cm;
public:
  ResetMarkingStateTask(G1ConcurrentMark* cm) : G1AbstractSubTask(G1GCPhaseTimes::ResetMarkingState), _cm(cm) { }

  double worker_cost() const override { return 1.0; }
  void do_work(uint worker_id) override;
};

class G1PreConcurrentStartTask::NoteStartOfMarkTask : public G1AbstractSubTask {
  HeapRegionClaimer _claimer;
public:
  NoteStartOfMarkTask() : G1AbstractSubTask(G1GCPhaseTimes::NoteStartOfMark), _claimer(0) { }

  double worker_cost() const override {
    // The work done per region is very small, therefore we choose this magic number to cap the number
    // of threads used when there are few regions.
    const double regions_per_thread = 1000;
    return _claimer.n_regions() / regions_per_thread;
  }

  void set_max_workers(uint max_workers) override;
  void do_work(uint worker_id) override;
};

void G1PreConcurrentStartTask::ResetMarkingStateTask::do_work(uint worker_id) {
  // Reset marking state.
  _cm->reset();
}

class NoteStartOfMarkHRClosure : public HeapRegionClosure {
public:
  bool do_heap_region(HeapRegion* r) override {
    r->note_start_of_marking();
    return false;
  }
};

void G1PreConcurrentStartTask::NoteStartOfMarkTask::do_work(uint worker_id) {
  NoteStartOfMarkHRClosure start_cl;
  G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&start_cl, &_claimer, worker_id);
}

void G1PreConcurrentStartTask::NoteStartOfMarkTask::set_max_workers(uint max_workers) {
  _claimer.set_n_workers(max_workers);
}

G1PreConcurrentStartTask::G1PreConcurrentStartTask(GCCause::Cause cause, G1ConcurrentMark* cm) :
  G1BatchedTask("Pre Concurrent Start", G1CollectedHeap::heap()->phase_times()) {
  add_serial_task(new ResetMarkingStateTask(cm));
  add_parallel_task(new NoteStartOfMarkTask());
};

void G1ConcurrentMark::pre_concurrent_start(GCCause::Cause cause) {
  assert_at_safepoint_on_vm_thread();

  G1CollectedHeap::start_codecache_marking_cycle_if_inactive();

  ClassLoaderDataGraph::verify_claimed_marks_cleared(ClassLoaderData::_claim_strong);

  G1PreConcurrentStartTask cl(cause, this);
  G1CollectedHeap::heap()->run_batch_task(&cl);

  _gc_tracer_cm->set_gc_cause(cause);
}


void G1ConcurrentMark::post_concurrent_mark_start() {
  // Start Concurrent Marking weak-reference discovery.
  ReferenceProcessor* rp = _g1h->ref_processor_cm();
  rp->start_discovery(false /* always_clear */);

  SATBMarkQueueSet& satb_mq_set = G1BarrierSet::satb_mark_queue_set();
  // This is the start of  the marking cycle, we're expected all
  // threads to have SATB queues with active set to false.
  satb_mq_set.set_active_all_threads(true/* new active value */
                                     false /* expected_active */);

  _root_regions.prepare_for_scan();

  // update_g1_committed() will be called at the end of an evac pause
  // when marking is on. So, it's also called at the end of the
  // concurrent start pause to update the heap end, if the heap expands
  // during it. No need to call it here.
}

void G1ConcurrentMark::post_concurrent_undo_start() {
  root_regions()->cancel_scan();
}

/*
 * Notice that in the next two methods, we actually leave the STS
 * during the barrier sync and join it immediately afterwards. If we
 * do not do this, the following deadlock can occur: one thread could
 * be in the barrier sync code, waiting for the other thread to also
 * sync up, whereas another one could be trying to yield, while also
 * waiting for the other threads to sync up too.
 *
 * Note, however, that this code is also used during remark and in
 * this case we should not attempt to leave / enter the STS, otherwise
 * we'll either hit an assert (debug / fastdebug) or deadlock
 * (product). So we should only leave / enter the STS if we are
 * operating concurrently.
 *
 * Because the thread that does the sync barrier has left the STS, it
 * is possible to be suspended for a Full GC or an evacuation pause
 * could occur. This is actually safe, since the entering the sync
 * barrier is one of the last things do_marking_step() does, and it
 * doesn't manipulate any data structures afterwards.
 */


void G1ConcurrentMark::enter_first_sync_barrier(uint worker_id) {
  bool barrier_aborted;
  {
    SuspendibleThreadSetLeaver sts_leave(concurrent());
    barrier_aborted = !_first_overflow_barrier_sync.enter();
  }

  // at this point everyone should have synced up and not be doing any
  // more work

  if (barrier_aborted) {
    // If the barrier aborted we ignore the overflow condition and
    // just abort the whole marking phase as quickly as possible.
    return;
  }
}

void G1ConcurrentMark::enter_second_sync_barrier(uint worker_id) {
  SuspendibleThreadSetLeaver sts_leave(concurrent());
  _second_overflow_barrier_sync.enter();

  // at this point everything should be re-initialized and ready to go
}

class G1CMConcurrentMarkingTask : public WorkerTask {
  G1ConcurrentMark*     _cm;

public:
  void work(uint worker_id) {
    ResourceMark rm;

    double start_vtime = os::elapsedVTime();

    {
      SuspendibleThreadSetJoiner sts_join;

      assert(worker_id < _cm->active_tasks(), "invariant");

      G1CMTask* task = _cm->task(worker_id);
      task->record_start_time();
      if (!_cm->has_aborted()) {
        do {
          task->do_marking_step(G1ConcMarkStepDurationMillis,
                                true  /* do_termination */,
                                false /* is_serial*/);

          _cm->do_yield_check();
        } while (!_cm->has_aborted() && task->has_aborted());
      }
      task->record_end_time();
      guarantee(!task->has_aborted() || _cm->has_aborted(), "invariant");
    }

    double end_vtime = os::elapsedVTime();
    _cm->update_accum_task_vtime(worker_id, end_vtime - start_vtime);
  }

  G1CMConcurrentMarkingTask(G1ConcurrentMark* cm) :
      WorkerTask("Concurrent Mark"), _cm(cm) { }

  ~G1CMConcurrentMarkingTask() { }
};

uint G1ConcurrentMark::calc_active_marking_workers() {
  uint result = 0;
  if (!UseDynamicNumberOfGCThreads || !FLAG_IS_DEFAULT(ConcGCThreads)) {
    result = _max_concurrent_workers;
  } else {
    result =
      WorkerPolicy::calc_default_active_workers(_max_concurrent_workers,
                                                1, /* Minimum workers */
                                                _num_concurrent_workers,
                                                Threads::number_of_non_daemon_threads());
    // Don't scale the result down by scale_concurrent_workers() because
    // that scaling has already gone into "_max_concurrent_workers".
  }
  assert(result > 0 && result <= _max_concurrent_workers,
         "Calculated number of marking workers must be larger than zero and at most the maximum %u, but is %u",
         _max_concurrent_workers, result);
  return result;
}

void G1ConcurrentMark::scan_root_region(const MemRegion* region, uint worker_id) {
#ifdef ASSERT
  HeapWord* last = region->last();
  HeapRegion* hr = _g1h->heap_region_containing(last);
  assert(hr->is_old() || hr->top_at_mark_start() == hr->bottom(),
         "Root regions must be old or survivor/eden but region %u is %s", hr->hrm_index(), hr->get_type_str());
  assert(hr->top_at_mark_start() == region->start(),
         "MemRegion start should be equal to TAMS");
#endif

  G1RootRegionScanClosure cl(_g1h, this, worker_id);

  const uintx interval = PrefetchScanIntervalInBytes;
  HeapWord* curr = region->start();
  const HeapWord* end = region->end();
  while (curr < end) {
    Prefetch::read(curr, interval);
    oop obj = cast_to_oop(curr);
    size_t size = obj->oop_iterate_size(&cl);
    assert(size == obj->size(), "sanity");
    curr += size;
  }
}

class G1CMRootRegionScanTask : public WorkerTask {
  G1ConcurrentMark* _cm;
public:
  G1CMRootRegionScanTask(G1ConcurrentMark* cm) :
    WorkerTask("G1 Root Region Scan"), _cm(cm) { }

  void work(uint worker_id) {
    G1CMRootMemRegions* root_regions = _cm->root_regions();
    const MemRegion* region = root_regions->claim_next();
    while (region != NULL) {
      _cm->scan_root_region(region, worker_id);
      region = root_regions->claim_next();
    }
  }
};

void G1ConcurrentMark::scan_root_regions() {
  // scan_in_progress() will have been set to true only if there was
  // at least one root region to scan. So, if it's false, we
  // should not attempt to do any further work.
  if (root_regions()->scan_in_progress()) {
    assert(!has_aborted(), "Aborting before root region scanning is finished not supported.");

    _num_concurrent_workers = MIN2(calc_active_marking_workers(),
                                   // We distribute work on a per-region basis, so starting
                                   // more threads than that is useless.
                                   root_regions()->num_root_regions());
    assert(_num_concurrent_workers <= _max_concurrent_workers,
           "Maximum number of marking threads exceeded");

    G1CMRootRegionScanTask task(this);
    log_debug(gc, ergo)("Running %s using %u workers for %u work units.",
                        task.name(), _num_concurrent_workers, root_regions()->num_root_regions());
    _concurrent_workers->run_task(&task, _num_concurrent_workers);

    // It's possible that has_aborted() is true here without actually
    // aborting the survivor scan earlier. This is OK as it's
    // mainly used for sanity checking.
    root_regions()->scan_finished();
  }
}

bool G1ConcurrentMark::wait_until_root_region_scan_finished() {
  return root_regions()->wait_until_scan_finished();
}

void G1ConcurrentMark::add_root_region(HeapRegion* r) {
  root_regions()->add(r->top_at_mark_start(), r->top());
}

void G1ConcurrentMark::concurrent_cycle_start() {
  _gc_timer_cm->register_gc_start();

  _gc_tracer_cm->report_gc_start(GCCause::_no_gc /* first parameter is not used */, _gc_timer_cm->gc_start());

  _g1h->trace_heap_before_gc(_gc_tracer_cm);
}

uint G1ConcurrentMark::completed_mark_cycles() const {
  return Atomic::load(&_completed_mark_cycles);
}

void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
  _g1h->collector_state()->set_clearing_bitmap(false);

  _g1h->trace_heap_after_gc(_gc_tracer_cm);

  if (mark_cycle_completed) {
    Atomic::inc(&_completed_mark_cycles, memory_order_relaxed);
  }

  if (has_aborted()) {
    log_info(gc, marking)("Concurrent Mark Abort");
    _gc_tracer_cm->report_concurrent_mode_failure();
  }

  _gc_timer_cm->register_gc_end();

  _gc_tracer_cm->report_gc_end(_gc_timer_cm->gc_end(), _gc_timer_cm->time_partitions());
}

void G1ConcurrentMark::mark_from_roots() {
  _restart_for_overflow = false;

  _num_concurrent_workers = calc_active_marking_workers();

  uint active_workers = MAX2(1U, _num_concurrent_workers);

  // Setting active workers is not guaranteed since fewer
  // worker threads may currently exist and more may not be
  // available.
  active_workers = _concurrent_workers->set_active_workers(active_workers);
  log_info(gc, task)("Using %u workers of %u for marking", active_workers, _concurrent_workers->max_workers());

  // Parallel task terminator is set in "set_concurrency_and_phase()"
  set_concurrency_and_phase(active_workers, true /* concurrent */);

  G1CMConcurrentMarkingTask marking_task(this);
  _concurrent_workers->run_task(&marking_task);
  print_stats();
}

const char* G1ConcurrentMark::verify_location_string(VerifyLocation location) {
  static const char* location_strings[] = { "Remark Before",
                                            "Remark After",
                                            "Remark Overflow",
                                            "Cleanup Before",
                                            "Cleanup After" };
  return location_strings[static_cast<std::underlying_type_t<VerifyLocation>>(location)];
}

void G1ConcurrentMark::verify_during_pause(G1HeapVerifier::G1VerifyType type,
                                           VerifyLocation location) {
  G1HeapVerifier* verifier = _g1h->verifier();

  verifier->verify_region_sets_optional();

  const char* caller = verify_location_string(location);

  if (VerifyDuringGC) {
    GCTraceTime(Debug, gc, phases) debug(caller, _gc_timer_cm);

    size_t const BufLen = 512;
    char buffer[BufLen];

    jio_snprintf(buffer, BufLen, "During GC (%s)", caller);
    verifier->verify(type, VerifyOption::G1UseConcMarking, buffer);

    // Only check bitmap in Remark, and not at After-Verification because the regions
    // already have their TAMS'es reset.
    if (location != VerifyLocation::RemarkAfter) {
      verifier->verify_bitmap_clear(true /* above_tams_only */);
    }
  }
}

class G1UpdateRemSetTrackingBeforeRebuildTask : public WorkerTask {
  G1CollectedHeap* _g1h;
  G1ConcurrentMark* _cm;
  HeapRegionClaimer _hrclaimer;
  uint volatile _total_selected_for_rebuild;

  G1PrintRegionLivenessInfoClosure _cl;

  class G1UpdateRemSetTrackingBeforeRebuild : public HeapRegionClosure {
    G1CollectedHeap* _g1h;
    G1ConcurrentMark* _cm;

    G1PrintRegionLivenessInfoClosure* _cl;

    uint _num_regions_selected_for_rebuild;  // The number of regions actually selected for rebuild.

    void update_remset_before_rebuild(HeapRegion* hr) {
      G1RemSetTrackingPolicy* tracking_policy = _g1h->policy()->remset_tracker();

      bool selected_for_rebuild;
      if (hr->is_humongous()) {
        bool const is_live = _cm->live_words(hr->humongous_start_region()->hrm_index()) > 0;
        selected_for_rebuild = tracking_policy->update_humongous_before_rebuild(hr, is_live);
      } else {
        size_t const live_bytes = _cm->live_bytes(hr->hrm_index());
        selected_for_rebuild = tracking_policy->update_before_rebuild(hr, live_bytes);
      }
      if (selected_for_rebuild) {
        _num_regions_selected_for_rebuild++;
      }
      _cm->update_top_at_rebuild_start(hr);
    }

    // Distribute the given words across the humongous object starting with hr and
    // note end of marking.
    void distribute_marked_bytes(HeapRegion* hr, size_t marked_words) {
      uint const region_idx = hr->hrm_index();
      size_t const obj_size_in_words = cast_to_oop(hr->bottom())->size();
      uint const num_regions_in_humongous = (uint)G1CollectedHeap::humongous_obj_size_in_regions(obj_size_in_words);

      // "Distributing" zero words means that we only note end of marking for these
      // regions.
      assert(marked_words == 0 || obj_size_in_words == marked_words,
             "Marked words should either be 0 or the same as humongous object (" SIZE_FORMAT ") but is " SIZE_FORMAT,
             obj_size_in_words, marked_words);

      for (uint i = region_idx; i < (region_idx + num_regions_in_humongous); i++) {
        HeapRegion* const r = _g1h->region_at(i);
        size_t const words_to_add = MIN2(HeapRegion::GrainWords, marked_words);

        log_trace(gc, marking)("Adding " SIZE_FORMAT " words to humongous region %u (%s)",
                               words_to_add, i, r->get_type_str());
        add_marked_bytes_and_note_end(r, words_to_add * HeapWordSize);
        marked_words -= words_to_add;
      }
      assert(marked_words == 0,
             SIZE_FORMAT " words left after distributing space across %u regions",
             marked_words, num_regions_in_humongous);
    }

    void update_marked_bytes(HeapRegion* hr) {
      uint const region_idx = hr->hrm_index();
      size_t const marked_words = _cm->live_words(region_idx);
      // The marking attributes the object's size completely to the humongous starts
      // region. We need to distribute this value across the entire set of regions a
      // humongous object spans.
      if (hr->is_humongous()) {
        assert(hr->is_starts_humongous() || marked_words == 0,
               "Should not have marked words " SIZE_FORMAT " in non-starts humongous region %u (%s)",
               marked_words, region_idx, hr->get_type_str());
        if (hr->is_starts_humongous()) {
          distribute_marked_bytes(hr, marked_words);
        }
      } else {
        log_trace(gc, marking)("Adding " SIZE_FORMAT " words to region %u (%s)", marked_words, region_idx, hr->get_type_str());
        add_marked_bytes_and_note_end(hr, _cm->live_bytes(region_idx));
      }
    }

    void add_marked_bytes_and_note_end(HeapRegion* hr, size_t marked_bytes) {
      hr->note_end_of_marking(marked_bytes);
      _cl->do_heap_region(hr);
    }

  public:
    G1UpdateRemSetTrackingBeforeRebuild(G1CollectedHeap* g1h, G1ConcurrentMark* cm, G1PrintRegionLivenessInfoClosure* cl) :
      _g1h(g1h), _cm(cm), _cl(cl), _num_regions_selected_for_rebuild(0) { }

    virtual bool do_heap_region(HeapRegion* r) {
      update_remset_before_rebuild(r);
      update_marked_bytes(r);

      return false;
    }

    uint num_selected_for_rebuild() const { return _num_regions_selected_for_rebuild; }
  };

public:
  G1UpdateRemSetTrackingBeforeRebuildTask(G1CollectedHeap* g1h, G1ConcurrentMark* cm, uint num_workers) :
    WorkerTask("G1 Update RemSet Tracking Before Rebuild"),
    _g1h(g1h), _cm(cm), _hrclaimer(num_workers), _total_selected_for_rebuild(0), _cl("Post-Marking") { }

  virtual void work(uint worker_id) {
    G1UpdateRemSetTrackingBeforeRebuild update_cl(_g1h, _cm, &_cl);
    _g1h->heap_region_par_iterate_from_worker_offset(&update_cl, &_hrclaimer, worker_id);
    Atomic::add(&_total_selected_for_rebuild, update_cl.num_selected_for_rebuild());
  }

  uint total_selected_for_rebuild() const { return _total_selected_for_rebuild; }

  // Number of regions for which roughly one thread should be spawned for this work.
  static const uint RegionsPerThread = 384;
};

class G1UpdateRegionsAfterRebuild : public HeapRegionClosure {
  G1CollectedHeap* _g1h;

public:
  G1UpdateRegionsAfterRebuild(G1CollectedHeap* g1h) :
    _g1h(g1h) {
  }

  virtual bool do_heap_region(HeapRegion* r) {
    // Update the remset tracking state from updating to complete
    // if remembered sets have been rebuilt.
    _g1h->policy()->remset_tracker()->update_after_rebuild(r);
    return false;
  }
};

void G1ConcurrentMark::remark() {
  assert_at_safepoint_on_vm_thread();

  // If a full collection has happened, we should not continue. However we might
  // have ended up here as the Remark VM operation has been scheduled already.
  if (has_aborted()) {
    return;
  }

  G1Policy* policy = _g1h->policy();
  policy->record_concurrent_mark_remark_start();

  double start = os::elapsedTime();

  verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkBefore);

  {
    GCTraceTime(Debug, gc, phases) debug("Finalize Marking", _gc_timer_cm);
    finalize_marking();
  }

  double mark_work_end = os::elapsedTime();

  bool const mark_finished = !has_overflown();
  if (mark_finished) {
    weak_refs_work();

    SATBMarkQueueSet& satb_mq_set = G1BarrierSet::satb_mark_queue_set();
    // We're done with marking.
    // This is the end of the marking cycle, we're expected all
    // threads to have SATB queues with active set to true.
    satb_mq_set.set_active_all_threads(false/* new active value */
                                       true /* expected_active */);

    {
      GCTraceTime(Debug, gc, phases) debug("Flush Task Caches", _gc_timer_cm);
      flush_all_task_caches();
    }

    // All marking completed. Check bitmap now as we will start to reset TAMSes
    // in parallel below so that we can not do this in the After-Remark verification.
    _g1h->verifier()->verify_bitmap_clear(true /* above_tams_only */);

    {
      GCTraceTime(Debug, gc, phases) debug("Update Remembered Set Tracking Before Rebuild", _gc_timer_cm);

      uint const workers_by_capacity = (_g1h->num_regions() + G1UpdateRemSetTrackingBeforeRebuildTask::RegionsPerThread - 1) /
                                       G1UpdateRemSetTrackingBeforeRebuildTask::RegionsPerThread;
      uint const num_workers = MIN2(_g1h->workers()->active_workers(), workers_by_capacity);

      G1UpdateRemSetTrackingBeforeRebuildTask cl(_g1h, this, num_workers);
      log_debug(gc,ergo)("Running %s using %u workers for %u regions in heap", cl.name(), num_workers, _g1h->num_regions());
      _g1h->workers()->run_task(&cl, num_workers);

      log_debug(gc, remset, tracking)("Remembered Set Tracking update regions total %u, selected %u",
                                      _g1h->num_regions(), cl.total_selected_for_rebuild());

      _needs_remembered_set_rebuild = (cl.total_selected_for_rebuild() > 0);
    }
    {
      GCTraceTime(Debug, gc, phases) debug("Reclaim Empty Regions", _gc_timer_cm);
      reclaim_empty_regions();
    }

    // Clean out dead classes
    if (ClassUnloadingWithConcurrentMark) {
      GCTraceTime(Debug, gc, phases) debug("Purge Metaspace", _gc_timer_cm);
      ClassLoaderDataGraph::purge(/*at_safepoint*/true);
    }

    _g1h->resize_heap_if_necessary();
    _g1h->uncommit_regions_if_necessary();

    compute_new_sizes();

    verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkAfter);

    assert(!restart_for_overflow(), "sanity");
    // Completely reset the marking state (except bitmaps) since marking completed.
    reset_at_marking_complete();
  } else {
    // We overflowed.  Restart concurrent marking.
    _restart_for_overflow = true;

    verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkOverflow);

    // Clear the marking state because we will be restarting
    // marking due to overflowing the global mark stack.
    reset_marking_for_restart();
  }

  {
    GCTraceTime(Debug, gc, phases) debug("Report Object Count", _gc_timer_cm);
    report_object_count(mark_finished);
  }

  CodeCache::on_gc_marking_cycle_finish();
  CodeCache::arm_all_nmethods();

  // Statistics
  double now = os::elapsedTime();
  _remark_mark_times.add((mark_work_end - start) * 1000.0);
  _remark_weak_ref_times.add((now - mark_work_end) * 1000.0);
  _remark_times.add((now - start) * 1000.0);

  policy->record_concurrent_mark_remark_end();
}

class G1ReclaimEmptyRegionsTask : public WorkerTask {
  // Per-region work during the Cleanup pause.
  class G1ReclaimEmptyRegionsClosure : public HeapRegionClosure {
    G1CollectedHeap* _g1h;
    size_t _freed_bytes;
    FreeRegionList* _local_cleanup_list;
    uint _old_regions_removed;
    uint _archive_regions_removed;
    uint _humongous_regions_removed;

  public:
    G1ReclaimEmptyRegionsClosure(G1CollectedHeap* g1h,
                                 FreeRegionList* local_cleanup_list) :
      _g1h(g1h),
      _freed_bytes(0),
      _local_cleanup_list(local_cleanup_list),
      _old_regions_removed(0),
      _archive_regions_removed(0),
      _humongous_regions_removed(0) { }

    size_t freed_bytes() { return _freed_bytes; }
    const uint old_regions_removed() { return _old_regions_removed; }
    const uint archive_regions_removed() { return _archive_regions_removed; }
    const uint humongous_regions_removed() { return _humongous_regions_removed; }

    bool do_heap_region(HeapRegion *hr) {
      if (hr->used() > 0 && hr->live_bytes() == 0 && !hr->is_young() && !hr->is_closed_archive()) {
        log_trace(gc)("Reclaimed empty old gen region %u (%s) bot " PTR_FORMAT,
                      hr->hrm_index(), hr->get_short_type_str(), p2i(hr->bottom()));
        _freed_bytes += hr->used();
        hr->set_containing_set(NULL);
        if (hr->is_humongous()) {
          _humongous_regions_removed++;
          _g1h->free_humongous_region(hr, _local_cleanup_list);
        } else if (hr->is_open_archive()) {
          _archive_regions_removed++;
          _g1h->free_region(hr, _local_cleanup_list);
        } else {
          _old_regions_removed++;
          _g1h->free_region(hr, _local_cleanup_list);
        }
        hr->clear_cardtable();
        _g1h->concurrent_mark()->clear_statistics_in_region(hr->hrm_index());
      }

      return false;
    }
  };

  G1CollectedHeap* _g1h;
  FreeRegionList* _cleanup_list;
  HeapRegionClaimer _hrclaimer;

public:
  G1ReclaimEmptyRegionsTask(G1CollectedHeap* g1h, FreeRegionList* cleanup_list, uint n_workers) :
    WorkerTask("G1 Cleanup"),
    _g1h(g1h),
    _cleanup_list(cleanup_list),
    _hrclaimer(n_workers) {
  }

  void work(uint worker_id) {
    FreeRegionList local_cleanup_list("Local Cleanup List");
    G1ReclaimEmptyRegionsClosure cl(_g1h, &local_cleanup_list);
    _g1h->heap_region_par_iterate_from_worker_offset(&cl, &_hrclaimer, worker_id);
    assert(cl.is_complete(), "Shouldn't have aborted!");

    // Now update the old/archive/humongous region sets
    _g1h->remove_from_old_gen_sets(cl.old_regions_removed(),
                                   cl.archive_regions_removed(),
                                   cl.humongous_regions_removed());
    {
      MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
      _g1h->decrement_summary_bytes(cl.freed_bytes());

      _cleanup_list->add_ordered(&local_cleanup_list);
      assert(local_cleanup_list.is_empty(), "post-condition");
    }
  }
};

void G1ConcurrentMark::reclaim_empty_regions() {
  WorkerThreads* workers = _g1h->workers();
  FreeRegionList empty_regions_list("Empty Regions After Mark List");

  G1ReclaimEmptyRegionsTask cl(_g1h, &empty_regions_list, workers->active_workers());
  workers->run_task(&cl);

  if (!empty_regions_list.is_empty()) {
    log_debug(gc)("Reclaimed %u empty regions", empty_regions_list.length());
    // Now print the empty regions list.
    _g1h->hr_printer()->cleanup(&empty_regions_list);
    // And actually make them available.
    _g1h->prepend_to_freelist(&empty_regions_list);
  }
}

void G1ConcurrentMark::compute_new_sizes() {
  MetaspaceGC::compute_new_size();

  // Cleanup will have freed any regions completely full of garbage.
  // Update the soft reference policy with the new heap occupancy.
  Universe::heap()->update_capacity_and_used_at_gc();

  // We reclaimed old regions so we should calculate the sizes to make
  // sure we update the old gen/space data.
  _g1h->monitoring_support()->update_sizes();
}

void G1ConcurrentMark::cleanup() {
  assert_at_safepoint_on_vm_thread();

  // If a full collection has happened, we shouldn't do this.
  if (has_aborted()) {
    return;
  }

  G1Policy* policy = _g1h->policy();
  policy->record_concurrent_mark_cleanup_start();

  double start = os::elapsedTime();

  verify_during_pause(G1HeapVerifier::G1VerifyCleanup, VerifyLocation::CleanupBefore);

  if (needs_remembered_set_rebuild()) {
    // Update the remset tracking information as well as marking all regions
    // as fully parsable.
    GCTraceTime(Debug, gc, phases) debug("Update Remembered Set Tracking After Rebuild", _gc_timer_cm);
    G1UpdateRegionsAfterRebuild cl(_g1h);
    _g1h->heap_region_iterate(&cl);
  } else {
    log_debug(gc, phases)("No Remembered Sets to update after rebuild");
  }

  verify_during_pause(G1HeapVerifier::G1VerifyCleanup, VerifyLocation::CleanupAfter);

  // We need to make this be a "collection" so any collection pause that
  // races with it goes around and waits for Cleanup to finish.
  _g1h->increment_total_collections();

  // Local statistics
  double recent_cleanup_time = (os::elapsedTime() - start);
  _total_cleanup_time += recent_cleanup_time;
  _cleanup_times.add(recent_cleanup_time);

  {
    GCTraceTime(Debug, gc, phases) debug("Finalize Concurrent Mark Cleanup", _gc_timer_cm);
    policy->record_concurrent_mark_cleanup_end(needs_remembered_set_rebuild());
  }
}

// 'Keep Alive' oop closure used by both serial parallel reference processing.
// Uses the G1CMTask associated with a worker thread (for serial reference
// processing the G1CMTask for worker 0 is used) to preserve (mark) and
// trace referent objects.
//
// Using the G1CMTask and embedded local queues avoids having the worker
// threads operating on the global mark stack. This reduces the risk
// of overflowing the stack - which we would rather avoid at this late
// state. Also using the tasks' local queues removes the potential
// of the workers interfering with each other that could occur if
// operating on the global stack.

class G1CMKeepAliveAndDrainClosure : public OopClosure {
  G1ConcurrentMark* _cm;
  G1CMTask*         _task;
  uint              _ref_counter_limit;
  uint              _ref_counter;
  bool              _is_serial;
public:
  G1CMKeepAliveAndDrainClosure(G1ConcurrentMark* cm, G1CMTask* task, bool is_serial) :
    _cm(cm), _task(task), _ref_counter_limit(G1RefProcDrainInterval),
    _ref_counter(_ref_counter_limit), _is_serial(is_serial) {
    assert(!_is_serial || _task->worker_id() == 0, "only task 0 for serial code");
  }

  virtual void do_oop(narrowOop* p) { do_oop_work(p); }
  virtual void do_oop(      oop* p) { do_oop_work(p); }

  template <class T> void do_oop_work(T* p) {
    if (_cm->has_overflown()) {
      return;
    }
    if (!_task->deal_with_reference(p)) {
      // We did not add anything to the mark bitmap (or mark stack), so there is
      // no point trying to drain it.
      return;
    }
    _ref_counter--;

    if (_ref_counter == 0) {
      // We have dealt with _ref_counter_limit references, pushing them
      // and objects reachable from them on to the local stack (and
      // possibly the global stack). Call G1CMTask::do_marking_step() to
      // process these entries.
      //
      // We call G1CMTask::do_marking_step() in a loop, which we'll exit if
      // there's nothing more to do (i.e. we're done with the entries that
      // were pushed as a result of the G1CMTask::deal_with_reference() calls
      // above) or we overflow.
      //
      // Note: G1CMTask::do_marking_step() can set the G1CMTask::has_aborted()
      // flag while there may still be some work to do. (See the comment at
      // the beginning of G1CMTask::do_marking_step() for those conditions -
      // one of which is reaching the specified time target.) It is only
      // when G1CMTask::do_marking_step() returns without setting the
      // has_aborted() flag that the marking step has completed.
      do {
        double mark_step_duration_ms = G1ConcMarkStepDurationMillis;
        _task->do_marking_step(mark_step_duration_ms,
                               false      /* do_termination */,
                               _is_serial);
      } while (_task->has_aborted() && !_cm->has_overflown());
      _ref_counter = _ref_counter_limit;
    }
  }
};

// 'Drain' oop closure used by both serial and parallel reference processing.
// Uses the G1CMTask associated with a given worker thread (for serial
// reference processing the G1CMtask for worker 0 is used). Calls the
// do_marking_step routine, with an unbelievably large timeout value,
// to drain the marking data structures of the remaining entries
// added by the 'keep alive' oop closure above.

class G1CMDrainMarkingStackClosure : public VoidClosure {
  G1ConcurrentMark* _cm;
  G1CMTask*         _task;
  bool              _is_serial;
 public:
  G1CMDrainMarkingStackClosure(G1ConcurrentMark* cm, G1CMTask* task, bool is_serial) :
    _cm(cm), _task(task), _is_serial(is_serial) {
    assert(!_is_serial || _task->worker_id() == 0, "only task 0 for serial code");
  }

  void do_void() {
    do {
      // We call G1CMTask::do_marking_step() to completely drain the local
      // and global marking stacks of entries pushed by the 'keep alive'
      // oop closure (an instance of G1CMKeepAliveAndDrainClosure above).
      //
      // G1CMTask::do_marking_step() is called in a loop, which we'll exit
      // if there's nothing more to do (i.e. we've completely drained the
      // entries that were pushed as a result of applying the 'keep alive'
      // closure to the entries on the discovered ref lists) or we overflow
      // the global marking stack.
      //
      // Note: G1CMTask::do_marking_step() can set the G1CMTask::has_aborted()
      // flag while there may still be some work to do. (See the comment at
      // the beginning of G1CMTask::do_marking_step() for those conditions -
      // one of which is reaching the specified time target.) It is only
      // when G1CMTask::do_marking_step() returns without setting the
      // has_aborted() flag that the marking step has completed.

      _task->do_marking_step(1000000000.0 /* something very large */,
                             true         /* do_termination */,
                             _is_serial);
    } while (_task->has_aborted() && !_cm->has_overflown());
  }
};

class G1CMRefProcProxyTask : public RefProcProxyTask {
  G1CollectedHeap& _g1h;
  G1ConcurrentMark& _cm;

public:
  G1CMRefProcProxyTask(uint max_workers, G1CollectedHeap& g1h, G1ConcurrentMark &cm)
    : RefProcProxyTask("G1CMRefProcProxyTask", max_workers),
      _g1h(g1h),
      _cm(cm) {}

  void work(uint worker_id) override {
    assert(worker_id < _max_workers, "sanity");
    G1CMIsAliveClosure is_alive(&_g1h);
    uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
    G1CMKeepAliveAndDrainClosure keep_alive(&_cm, _cm.task(index), _tm == RefProcThreadModel::Single);
    BarrierEnqueueDiscoveredFieldClosure enqueue;
    G1CMDrainMarkingStackClosure complete_gc(&_cm, _cm.task(index), _tm == RefProcThreadModel::Single);
    _rp_task->rp_work(worker_id, &is_alive, &keep_alive, &enqueue, &complete_gc);
  }

  void prepare_run_task_hook() override {
    // We need to reset the concurrency level before each
    // proxy task execution, so that the termination protocol
    // and overflow handling in G1CMTask::do_marking_step() knows
    // how many workers to wait for.
    _cm.set_concurrency(_queue_count);
  }
};

void G1ConcurrentMark::weak_refs_work() {
  ResourceMark rm;

  // Is alive closure.
  G1CMIsAliveClosure g1_is_alive(_g1h);

  {
    GCTraceTime(Debug, gc, phases) debug("Reference Processing", _gc_timer_cm);

    ReferenceProcessor* rp = _g1h->ref_processor_cm();

    // See the comment in G1CollectedHeap::ref_processing_init()
    // about how reference processing currently works in G1.

    assert(_global_mark_stack.is_empty(), "mark stack should be empty");

    // We need at least one active thread. If reference processing
    // is not multi-threaded we use the current (VMThread) thread,
    // otherwise we use the workers from the G1CollectedHeap and
    // we utilize all the worker threads we can.
    uint active_workers = (ParallelRefProcEnabled ? _g1h->workers()->active_workers() : 1U);
    active_workers = clamp(active_workers, 1u, _max_num_tasks);

    // Set the degree of MT processing here.  If the discovery was done MT,
    // the number of threads involved during discovery could differ from
    // the number of active workers.  This is OK as long as the discovered
    // Reference lists are balanced (see balance_all_queues() and balance_queues()).
    rp->set_active_mt_degree(active_workers);

    // Parallel processing task executor.
    G1CMRefProcProxyTask task(rp->max_num_queues(), *_g1h, *this);
    ReferenceProcessorPhaseTimes pt(_gc_timer_cm, rp->max_num_queues());

    // Process the weak references.
    const ReferenceProcessorStats& stats = rp->process_discovered_references(task, pt);
    _gc_tracer_cm->report_gc_reference_stats(stats);
    pt.print_all_references();

    // The do_oop work routines of the keep_alive and drain_marking_stack
    // oop closures will set the has_overflown flag if we overflow the
    // global marking stack.

    assert(has_overflown() || _global_mark_stack.is_empty(),
           "Mark stack should be empty (unless it has overflown)");

    assert(rp->num_queues() == active_workers, "why not");
  }

  if (has_overflown()) {
    // We can not trust g1_is_alive and the contents of the heap if the marking stack
    // overflowed while processing references. Exit the VM.
    fatal("Overflow during reference processing, can not continue. Current mark stack depth: "
          SIZE_FORMAT ", MarkStackSize: " SIZE_FORMAT ", MarkStackSizeMax: " SIZE_FORMAT ". "
          "Please increase MarkStackSize and/or MarkStackSizeMax and restart.",
          _global_mark_stack.size(), MarkStackSize, MarkStackSizeMax);
    return;
  }

  assert(_global_mark_stack.is_empty(), "Marking should have completed");

  {
    GCTraceTime(Debug, gc, phases) debug("Weak Processing", _gc_timer_cm);
    WeakProcessor::weak_oops_do(_g1h->workers(), &g1_is_alive, &do_nothing_cl, 1);
  }

  // Unload Klasses, String, Code Cache, etc.
  if (ClassUnloadingWithConcurrentMark) {
    GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
    CodeCache::UnloadingScope scope(&g1_is_alive);
    bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm);
    _g1h->complete_cleaning(purged_classes);
  }
}

class G1PrecleanYieldClosure : public YieldClosure {
  G1ConcurrentMark* _cm;

public:
  G1PrecleanYieldClosure(G1ConcurrentMark* cm) : _cm(cm) { }

  virtual bool should_return() {
    return _cm->has_aborted();
  }

  virtual bool should_return_fine_grain() {
    _cm->do_yield_check();
    return _cm->has_aborted();
  }
};

void G1ConcurrentMark::preclean() {
  assert(G1UseReferencePrecleaning, "Precleaning must be enabled.");

  SuspendibleThreadSetJoiner joiner;

  BarrierEnqueueDiscoveredFieldClosure enqueue;

  set_concurrency_and_phase(1, true);

  G1PrecleanYieldClosure yield_cl(this);

  ReferenceProcessor* rp = _g1h->ref_processor_cm();
  // Precleaning is single threaded. Temporarily disable MT discovery.
  ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(rp, false);
  rp->preclean_discovered_references(rp->is_alive_non_header(),
                                     &enqueue,
                                     &yield_cl,
                                     _gc_timer_cm);
}

class G1ObjectCountIsAliveClosure: public BoolObjectClosure {
  G1CollectedHeap* _g1h;
public:
  G1ObjectCountIsAliveClosure(G1CollectedHeap* g1h) : _g1h(g1h) { }

  bool do_object_b(oop obj) {
    return obj != NULL &&
           (!_g1h->is_in_reserved(obj) || !_g1h->is_obj_dead(obj));
  }
};

void G1ConcurrentMark::report_object_count(bool mark_completed) {
  // Depending on the completion of the marking liveness needs to be determined
  // using either the bitmap or after the cycle using the scrubbing information.
  if (mark_completed) {
    G1ObjectCountIsAliveClosure is_alive(_g1h);
    _gc_tracer_cm->report_object_count_after_gc(&is_alive);
  } else {
    G1CMIsAliveClosure is_alive(_g1h);
    _gc_tracer_cm->report_object_count_after_gc(&is_alive);
  }
}

// Closure for marking entries in SATB buffers.
class G1CMSATBBufferClosure : public SATBBufferClosure {
private:
  G1CMTask* _task;
  G1CollectedHeap* _g1h;

  // This is very similar to G1CMTask::deal_with_reference, but with
  // more relaxed requirements for the argument, so this must be more
  // circumspect about treating the argument as an object.
  void do_entry(void* entry) const {
    _task->increment_refs_reached();
    oop const obj = cast_to_oop(entry);
    _task->make_reference_grey(obj);
  }

public:
  G1CMSATBBufferClosure(G1CMTask* task, G1CollectedHeap* g1h)
    : _task(task), _g1h(g1h) { }

  virtual void do_buffer(void** buffer, size_t size) {
    for (size_t i = 0; i < size; ++i) {
      do_entry(buffer[i]);
    }
  }
};

class G1RemarkThreadsClosure : public ThreadClosure {
  G1SATBMarkQueueSet& _qset;
  G1CMOopClosure _cm_cl;
  MarkingCodeBlobClosure _code_cl;
  uintx _claim_token;

 public:
  G1RemarkThreadsClosure(G1CollectedHeap* g1h, G1CMTask* task) :
    _qset(G1BarrierSet::satb_mark_queue_set()),
    _cm_cl(g1h, task),
    _code_cl(&_cm_cl, !CodeBlobToOopClosure::FixRelocations, true /* keepalive nmethods */),
    _claim_token(Threads::thread_claim_token()) {}

  void do_thread(Thread* thread) {
    if (thread->claim_threads_do(true, _claim_token)) {
      // Transfer any partial buffer to the qset for completed buffer processing.
      _qset.flush_queue(G1ThreadLocalData::satb_mark_queue(thread));
      if (thread->is_Java_thread()) {
        // In theory it should not be necessary to explicitly walk the nmethods to find roots for concurrent marking
        // however the liveness of oops reachable from nmethods have very complex lifecycles:
        // * Alive if on the stack of an executing method
        // * Weakly reachable otherwise
        // Some objects reachable from nmethods, such as the class loader (or klass_holder) of the receiver should be
        // live by the SATB invariant but other oops recorded in nmethods may behave differently.
        JavaThread::cast(thread)->nmethods_do(&_code_cl);
      }
    }
  }
};

class G1CMRemarkTask : public WorkerTask {
  G1ConcurrentMark* _cm;
public:
  void work(uint worker_id) {
    G1CMTask* task = _cm->task(worker_id);
    task->record_start_time();
    {
      ResourceMark rm;

      G1RemarkThreadsClosure threads_f(G1CollectedHeap::heap(), task);
      Threads::threads_do(&threads_f);
    }

    do {
      task->do_marking_step(1000000000.0 /* something very large */,
                            true         /* do_termination       */,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=90 G=89

¤ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.