Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  mark_compact.h

  Sprache: C
 

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#ifndef ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_
#define ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_

#include <signal.h>

#include <map>
#include <memory>
#include <unordered_set>

#include "barrier.h"
#include "base/atomic.h"
#include "base/bit_vector.h"
#include "base/gc_visited_arena_pool.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/offsets.h"
#include "garbage_collector.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/bitmap-inl.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc_root.h"
#include "immune_spaces.h"
#include "scoped_thread_priority_change.h"

namespace art HIDDEN {

EXPORT bool KernelSupportsUffd();

namespace mirror {
class DexCache;
}  // namespace mirror

namespace gc {

class Heap;

namespace space {
class BumpPointerSpace;
}  // namespace space

namespace collector {
class MarkCompact;

// The actual young GC code is also implemented in MarkCompact class. However,
// using this class saves us from creating duplicate data-structures, which
// would have happened with two instances of MarkCompact.
class YoungMarkCompact final : public GarbageCollector {
 public:
  YoungMarkCompact(Heap* heap, MarkCompact* main);

  void RunPhases() override REQUIRES(!Locks::mutator_lock_);

  GcType GetGcType() const override { return kGcTypeSticky; }

  CollectorType GetCollectorType() const override { return kCollectorTypeCMC; }

  // None of the following methods are ever called as actual GC is performed by MarkCompact.

  mirror::Object* MarkObject([[maybe_unused]] mirror::Object* obj) override {
    UNIMPLEMENTED(FATAL);
    UNREACHABLE();
  }
  void MarkHeapReference([[maybe_unused]] mirror::HeapReference<mirror::Object>* obj,
                         [[maybe_unused]] bool do_atomic_update) override {
    UNIMPLEMENTED(FATAL);
  }
  void VisitRoots([[maybe_unused]] mirror::Object*** roots,
                  [[maybe_unused]] size_t count,
                  [[maybe_unused]] const RootInfo& info) override {
    UNIMPLEMENTED(FATAL);
  }
  void VisitRoots([[maybe_unused]] mirror::CompressedReference<mirror::Object>** roots,
                  [[maybe_unused]] size_t count,
                  [[maybe_unused]] const RootInfo& info) override {
    UNIMPLEMENTED(FATAL);
  }
  bool IsNullOrMarkedHeapReference(
      [[maybe_unused]] mirror::HeapReference<mirror::Object>* obj) override {
    UNIMPLEMENTED(FATAL);
    UNREACHABLE();
  }
  void RevokeAllThreadLocalBuffers() override { UNIMPLEMENTED(FATAL); }

  void DelayReferenceReferent([[maybe_unused]] ObjPtr<mirror::Class> klass,
                              [[maybe_unused]] ObjPtr<mirror::Reference> reference) override {
    UNIMPLEMENTED(FATAL);
  }
  mirror::Object* IsMarked([[maybe_unused]] mirror::Object* obj) override {
    UNIMPLEMENTED(FATAL);
    UNREACHABLE();
  }
  void ProcessMarkStack() override { UNIMPLEMENTED(FATAL); }

 private:
  MarkCompact* const main_collector_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(YoungMarkCompact);
};

class MarkCompact final : public GarbageCollector {
 public:
  using SigbusCounterType = uint32_t;

  static constexpr size_t kAlignment = kObjectAlignment;
  static constexpr int kUffdMode = -1;
  // Fake file descriptor for fall back mode (when uffd isn't available)
  static constexpr int kFallbackMode = -3;
  static constexpr int kFdUnused = -2;

  // Bitmask for the compaction-done bit in the sigbus_in_progress_count_.
  static constexpr SigbusCounterType kSigbusCounterCompactionDoneMask =
      1u << (BitSizeOf<SigbusCounterType>() - 1);

  explicit MarkCompact(Heap* heap);

  ~MarkCompact() {}

  void RunPhases() override REQUIRES(!Locks::mutator_lock_, !lock_);

  void ClampGrowthLimit(size_t new_capacity) REQUIRES(Locks::heap_bitmap_lock_);
  // Updated before (or in) pre-compaction pause and is accessed only in the
  // pause or during concurrent compaction. The flag is reset in next GC cycle's
  // InitializePhase(). Therefore, it's safe to update without any memory ordering.
  bool IsCompacting() const { return compacting_; }

  // Called by SIGBUS handler. NO_THREAD_SAFETY_ANALYSIS for mutator-lock, which
  // is asserted in the function.
  bool SigbusHandler(siginfo_t* info) REQUIRES(!lock_) NO_THREAD_SAFETY_ANALYSIS;
  // Called by SIGSYS handler to detect seccomp deny-listed syscalls.
  bool SigsysHandler(siginfo_t* info, void* context);

  GcType GetGcType() const override { return kGcTypePartial; }

  CollectorType GetCollectorType() const override {
    return kCollectorTypeCMC;
  }

  Barrier& GetBarrier() {
    return gc_barrier_;
  }

  mirror::Object* MarkObject(mirror::Object* obj) override
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj,
                         bool do_atomic_update) override
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  void VisitRoots(mirror::Object*** roots,
                  size_t count,
                  const RootInfo& info) override
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  void VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
                  size_t count,
                  const RootInfo& info) override
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj) override
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  void RevokeAllThreadLocalBuffers() override;

  void DelayReferenceReferent(ObjPtr<mirror::Class> klass,
                              ObjPtr<mirror::Reference> reference) override
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  mirror::Object* IsMarked(mirror::Object* obj) override
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  mirror::Object* GetFromSpaceAddrFromBarrier(mirror::Object* old_ref) const {
    CHECK(compacting_);
    return GetFromAddrAllSpaces(old_ref);
  }

  // Called from Heap::PostForkChildAction() for non-zygote processes and from
  // PrepareForCompaction() for zygote processes. Returns true if uffd was
  // created or was already done.
  bool CreateUserfaultfd(bool post_fork);

  // Returns a pair indicating if userfaultfd itself is available (first) and if
  // so then whether its minor-fault feature is available or not (second).
  static std::pair<boolbool> GetUffdAndMinorFault();

  // Add linear-alloc space data when a new space is added to
  // GcVisitedArenaPool, which mostly happens only once.
  void AddLinearAllocSpaceData(uint8_t* begin, size_t len);

  // Called by Heap::PreZygoteFork() to reset generational heap pointers and
  // other data structures as the moving space gets completely evicted into new
  // zygote-space.
  void ResetGenerationalState();

  // See comment for the following Getters and Setters below at the declaration
  // of 'moving_space_pages_info_'.
  uint32_t GetPreCompactMovingSpaceOffsets(size_t idx) const {
    return moving_space_pages_info_[idx];
  }
  void SetPreCompactMovingSpaceOffsets(size_t idx, uint32_t val) {
    moving_space_pages_info_[idx] = val;
  }
  uint32_t GetBlackAllocPagesFirstChunkSize(size_t idx) const {
    return moving_space_pages_info_[idx];
  }
  void SetBlackAllocPagesFirstChunkSize(size_t idx, uint32_t val) {
    moving_space_pages_info_[idx] = val;
  }
  uint32_t* GetMovingSpacePagesLiveBytesArr() { return moving_space_pages_info_; }

  // In copy-mode of userfaultfd, we don't need to reach a 'processed' state as
  // it's given that processing thread also copies the page, thereby mapping it.
  // The order is important as we may treat them as integers. Also
  // 'kUnprocessed' should be set to 0 as we rely on madvise(dontneed) to return
  // us zero'ed pages, which implicitly makes page-status initialized to 'kUnprocessed'.
  enum class PageState : uint8_t {
    kUnprocessed = 0,           // Not processed yet.
    kProcessing = 1,            // Being processed by GC thread and will not be mapped
    kProcessed = 2,             // Processed but not mapped
    kProcessingAndMapping = 3,  // Being processed by GC or mutator and will be mapped
    kMutatorProcessing = 4,     // Being processed by mutator thread
    kProcessedAndMapping = 5,   // Processed and will be mapped
    kProcessedAndMapped = 6     // Processed and mapped. For SIGBUS.
  };

  // Different heap clamping states.
  enum class ClampInfoStatus : uint8_t {
    kClampInfoNotDone,
    kClampInfoPending,
    kClampInfoFinished
  };

  friend void YoungMarkCompact::RunPhases();

 private:
  using ObjReference = mirror::CompressedReference<mirror::Object>;
  static constexpr uint32_t kPageStateMask = (1 << BitSizeOf<uint8_t>()) - 1;
  // Number of bits (live-words) covered by a single chunk-info (below)
  // entry/word.
  // TODO: Since popcount is performed usomg SIMD instructions, we should
  // consider using 128-bit in order to halve the chunk-info size.
  static constexpr uint32_t kBitsPerVectorWord = kBitsPerIntPtrT;
  static constexpr uint32_t kOffsetChunkSize = kBitsPerVectorWord * kAlignment;
  static_assert(kOffsetChunkSize < kMinPageSize);

  class RefFieldsVisitor;
  // Bitmap with bits corresponding to every live word set. For an object
  // which is 4 words in size will have the corresponding 4 bits set. This is
  // required for efficient computation of new-address (post-compaction) from
  // the given old-address (pre-compaction).
  template <size_t kAlignment>
  class LiveWordsBitmap : private accounting::MemoryRangeBitmap<kAlignment> {
    using Bitmap = accounting::Bitmap;
    using MemRangeBitmap = accounting::MemoryRangeBitmap<kAlignment>;

   public:
    static_assert(IsPowerOfTwo(kBitsPerVectorWord));
    static_assert(IsPowerOfTwo(Bitmap::kBitsPerBitmapWord));
    static_assert(kBitsPerVectorWord >= Bitmap::kBitsPerBitmapWord);
    static constexpr uint32_t kBitmapWordsPerVectorWord =
            kBitsPerVectorWord / Bitmap::kBitsPerBitmapWord;
    static_assert(IsPowerOfTwo(kBitmapWordsPerVectorWord));
    using MemRangeBitmap::SetBitmapSize;
    static LiveWordsBitmap* Create(uintptr_t begin, uintptr_t end);

    // Return offset (within the indexed chunk-info) of the nth live word.
    uint32_t FindNthLiveWordOffset(size_t chunk_idx, uint32_t n) const;
    // Sets all bits in the bitmap corresponding to the given range. Also
    // returns the bit-index of the first word.
    ALWAYS_INLINE uintptr_t SetLiveWords(uintptr_t begin, size_t size);
    // Count number of live words upto the given bit-index. This is to be used
    // to compute the post-compact address of an old reference.
    ALWAYS_INLINE size_t CountLiveWordsUpto(size_t bit_idx) const;
    // Call 'visitor' for every stride of contiguous marked bits in the live-words
    // bitmap, starting from begin_bit_idx. Only visit 'bytes' live bytes or
    // until 'end', whichever comes first.
    // Visitor is called with index of the first marked bit in the stride,
    // stride size and whether it's the last stride in the given range or not.
    template <typename Visitor>
    ALWAYS_INLINE void VisitLiveStrides(uintptr_t begin_bit_idx,
                                        uint8_t* end,
                                        const size_t bytes,
                                        Visitor&& visitor) const
        REQUIRES_SHARED(Locks::mutator_lock_);
    // Count the number of live bytes in the given vector entry.
    size_t LiveBytesInBitmapWord(size_t chunk_idx) const;
    void ClearBitmap() { Bitmap::Clear(); }
    ALWAYS_INLINE uintptr_t Begin() const { return MemRangeBitmap::CoverBegin(); }
    ALWAYS_INLINE bool HasAddress(mirror::Object* obj) const {
      return MemRangeBitmap::HasAddress(reinterpret_cast<uintptr_t>(obj));
    }
    ALWAYS_INLINE bool Test(uintptr_t bit_index) const {
      return Bitmap::TestBit(bit_index);
    }
    ALWAYS_INLINE bool Test(mirror::Object* obj) const {
      return MemRangeBitmap::Test(reinterpret_cast<uintptr_t>(obj));
    }
    ALWAYS_INLINE uintptr_t GetWord(size_t index) const {
      static_assert(kBitmapWordsPerVectorWord == 1);
      return Bitmap::Begin()[index * kBitmapWordsPerVectorWord];
    }
  };

  static bool HasAddress(uint8_t* addr, uint8_t* begin, uint8_t* end) {
    return addr >= begin && addr < end;
  }

  static bool HasAddress(void* obj, uint8_t* begin, uint8_t* end) {
    return HasAddress(reinterpret_cast<uint8_t*>(obj), begin, end);
  }

  bool HasAddress(void* obj) const {
    return HasAddress(obj, moving_space_begin_, moving_space_end_);
  }
  // For a given object address in pre-compact space, return the corresponding
  // address in the from-space, where heap pages are relocated in the compaction
  // pause.
  template <typename T>
  T* GetFromSpaceAddr(T* obj) const {
    DCHECK(HasAddress(obj)) << " obj=" << obj;
    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(obj) + from_space_slide_diff_);
  }

  mirror::Object* GetFromAddrAllSpaces(mirror::Object* old_ref) const {
    if (HasAddress(old_ref)) {
      return GetFromSpaceAddr(old_ref);
    }
    return old_ref;
  }

  template <typename T>
  inline T* GetToSpaceAddr(T* addr) const {
    DCHECK(from_space_map_.HasAddress(addr));
    return reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(addr) - from_space_slide_diff_);
  }

  inline bool IsOnAllocStack(mirror::Object* ref)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
  // Verifies that that given object reference refers to a valid object.
  // Otherwise fataly dumps logs, including those from callback.
  template <typename Callback>
  void VerifyObject(mirror::Object* ref, Callback& callback) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  void InitializePhase();
  void FinishPhase(bool performed_compaction)
      REQUIRES(!Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !lock_);
  void MarkingPhase() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
  void CompactionPhase() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);

  void SweepSystemWeaks(Thread* self, Runtime* runtime, const bool paused)
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(!Locks::heap_bitmap_lock_);
  // Update the reference at 'offset' in 'obj' with post-compact address, and
  // return the new address. [begin, end) is a range in which compaction is
  // happening. So post-compact address needs to be computed only for
  // pre-compact references in this range.
  ALWAYS_INLINE mirror::Object* UpdateRef(mirror::Object* obj,
                                          MemberOffset offset,
                                          uint8_t* begin,
                                          uint8_t* end) REQUIRES_SHARED(Locks::mutator_lock_);

  // Verify that the gc-root is updated only once. Returns false if the update
  // shouldn't be done.
  ALWAYS_INLINE bool VerifyRootSingleUpdate(void* root,
                                            mirror::Object* old_ref,
                                            const RootInfo& info)
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Update the given root with post-compact address and return the new address. [begin, end)
  // is a range in which compaction is happening. So post-compact address needs to be computed
  // only for pre-compact references in this range.
  ALWAYS_INLINE mirror::Object* UpdateRoot(mirror::CompressedReference<mirror::Object>* root,
                                           uint8_t* begin,
                                           uint8_t* end,
                                           const RootInfo& info = RootInfo(RootType::kRootUnknown))
      REQUIRES_SHARED(Locks::mutator_lock_);
  ALWAYS_INLINE mirror::Object* UpdateRoot(mirror::Object** root,
                                           uint8_t* begin,
                                           uint8_t* end,
                                           const RootInfo& info = RootInfo(RootType::kRootUnknown))
      REQUIRES_SHARED(Locks::mutator_lock_);
  // If the given pre-compact address (old_ref) is in [begin, end) range of moving-space,
  // then the function returns the computed post-compact address. Otherwise, 'old_ref' is
  // returned.
  ALWAYS_INLINE mirror::Object* PostCompactAddress(mirror::Object* old_ref,
                                                   uint8_t* begin,
                                                   uint8_t* end) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Compute post-compact address of an object in moving space. This function
  // assumes that old_ref is in moving space.
  ALWAYS_INLINE mirror::Object* PostCompactAddressUnchecked(mirror::Object* old_ref) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Compute the new address for an object which was allocated prior to starting
  // this GC cycle.
  ALWAYS_INLINE mirror::Object* PostCompactOldObjAddr(mirror::Object* old_ref) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Compute the new address for an object which was black allocated during this
  // GC cycle.
  ALWAYS_INLINE mirror::Object* PostCompactBlackObjAddr(mirror::Object* old_ref) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Clears (for alloc spaces in the beginning of marking phase) or ages the
  // card table. Also, identifies immune spaces and mark bitmap.
  void PrepareForMarking(bool pre_marking) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  // Perform one last round of marking, identifying roots from dirty cards
  // during a stop-the-world (STW) pause.
  void MarkingPause() REQUIRES(!Locks::mutator_lock_, !Locks::heap_bitmap_lock_);
  // Perform stop-the-world pause prior to concurrent compaction.
  // Updates GC-roots and protects heap so that during the concurrent
  // compaction phase we can receive faults and compact the corresponding pages
  // on the fly.
  void CompactionPause() REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_);
  // Compute offsets (in chunk_info_vec_) and other data structures required
  // during concurrent compaction. Also determines a black-dense region at the
  // beginning of the moving space which is not compacted. Returns false if
  // performing compaction isn't required.
  bool PrepareForCompaction() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(!Locks::heap_bitmap_lock_);

  // Copy gPageSize live bytes starting from 'offset' (within the moving space),
  // which must be within 'obj', into the gPageSize sized memory pointed by 'addr'.
  // Then update the references within the copied objects. The boundary objects are
  // partially updated such that only the references that lie in the page are updated.
  // This is necessary to avoid cascading userfaults.
  template <bool kSetupForGenerational>
  void CompactPage(mirror::Object* obj,
                   uint32_t offset,
                   uint8_t* addr,
                   uint8_t* to_space_addr,
                   bool needs_memset_zero)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
  // Compact the bump-pointer space. Pass page that should be used as buffer for
  // userfaultfd.
  template <int kMode>
  void CompactMovingSpace(uint8_t* page)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Compact the given page as per func and change its state. Also map/copy the
  // page, if required. Returns true if the page was compacted, else false.
  template <int kMode, typename CompactionFn>
  ALWAYS_INLINE bool DoPageCompactionWithStateChange(size_t page_idx,
                                                     uint8_t* to_space_page,
                                                     uint8_t* page,
                                                     bool map_immediately,
                                                     CompactionFn func)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Update all the objects in the given non-moving page. 'first' object
  // could have started in some preceding page. 'kObjInBlackDense' is true when
  // called for black-dense/old-gen pages, and false when called for non-moving
  // space pages.
  template <bool kSetupForGenerational, bool kObjInBlackDense>
  void UpdateNonMovingPage(mirror::Object* first,
                           uint8_t* page,
                           ptrdiff_t from_space_diff,
                           accounting::ContinuousSpaceBitmap* bitmap)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
  // Update all the references in the non-moving space.
  void UpdateNonMovingSpace() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // For all the pages in non-moving space, find the first object that overlaps
  // with the pages' start address, and store in first_objs_non_moving_space_ array.
  size_t InitNonMovingFirstObjects(uintptr_t begin,
                                   uintptr_t end,
                                   accounting::ContinuousSpaceBitmap* bitmap,
                                   ObjReference* first_objs_arr)
      REQUIRES_SHARED(Locks::mutator_lock_);
  // In addition to the first-objects for every post-compact moving space page,
  // also find offsets within those objects from where the contents should be
  // copied to the page. The offsets are relative to the moving-space's
  // beginning. Store the computed first-object and offset in first_objs_moving_space_
  // and pre_compact_offset_moving_space_ respectively.
  void InitMovingSpaceFirstObjects(size_t vec_len, size_t to_space_page_idx)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Gather the info related to black allocations from bump-pointer space to
  // enable concurrent sliding of these pages.
  void UpdateMovingSpaceBlackAllocations() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
  // Update first-object info from allocation-stack for non-moving space black
  // allocations.
  void UpdateNonMovingSpaceBlackAllocations() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Slides (retain the empty holes, which are usually part of some in-use TLAB)
  // black page in the moving space. 'first_obj' is the object that overlaps with
  // the first byte of the page being slid. pre_compact_page is the pre-compact
  // address of the page being slid. 'dest' is the gPageSize sized memory where
  // the contents would be copied.
  void SlideBlackPage(mirror::Object* first_obj,
                      mirror::Object* next_page_first_obj,
                      uint32_t first_chunk_size,
                      uint8_t* const pre_compact_page,
                      uint8_t* dest,
                      bool needs_memset_zero)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Perform reference-processing and the likes before sweeping the non-movable
  // spaces. Priority is reset once we no longer block reference operations.
  void ReclaimPhase(ScopedPriorityChange* spc) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(!Locks::heap_bitmap_lock_);

  // Mark GC-roots (except from immune spaces and thread-stacks) during a STW pause.
  void ReMarkRoots(Runtime* runtime) REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
  // Concurrently mark GC-roots, except from immune spaces.
  void MarkRoots(VisitRootFlags flags) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Collect thread stack roots via a checkpoint.
  void MarkRootsCheckpoint(Thread* self, Runtime* runtime) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Second round of concurrent marking. Mark all gray objects that got dirtied
  // since the first round.
  void PreCleanCards() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  void MarkNonThreadRoots(Runtime* runtime) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  void MarkConcurrentRoots(VisitRootFlags flags, Runtime* runtime)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  // Traverse through the reachable objects and mark them.
  void MarkReachableObjects() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Scan (only) immune spaces looking for references into the garbage collected
  // spaces.
  NO_INLINE void UpdateAndMarkModUnion() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Scan mod-union and card tables, covering all the spaces, to identify dirty objects.
  // These are in 'minimum age' cards, which is 'kCardAged' in case of concurrent (second round)
  // marking and kCardDirty during the STW pause.
  void ScanDirtyObjects(bool paused, uint8_t minimum_age) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Recursively mark dirty objects. Invoked both concurrently as well in a STW
  // pause in PausePhase().
  void RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age)
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Go through all the objects in the mark-stack until it's empty.
  NO_INLINE void ProcessMarkStack() override REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  void ExpandMarkStack() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  // Try re-loading class from 'obj' in case it shows up (See b/373609505)
  mirror::Class* ReloadScanObjClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Scan object for references. If kUpdateLivewords is true then set bits in
  // the live-words bitmap and add size to chunk-info.
  template <bool kUpdateLiveWords>
  ALWAYS_INLINE void ScanObject(mirror::Object* obj, const RefFieldsVisitor& visitor)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  NO_INLINE void ColdScanObject(mirror::Object* obj, const RefFieldsVisitor& visitor)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
    return ScanObject</*kUpdateLiveWords=*/true>(obj, visitor);
  }
  // Push objects to the mark-stack right after successfully marking objects.
  void PushOnMarkStack(mirror::Object* obj)
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  // Update the live-words bitmap as well as add the object size to the
  // chunk-info vector. Both are required for computation of post-compact addresses.
  // Also updates freed_objects_ counter.
  SINGLE_CALLER void UpdateLivenessInfo(mirror::Object* obj, size_t obj_size)
      REQUIRES_SHARED(Locks::mutator_lock_);

  void ProcessReferences(Thread* self)
      REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(!Locks::heap_bitmap_lock_);

  ALWAYS_INLINE void MarkObjectNonNull(mirror::Object* obj,
                                       mirror::Object* holder = nullptr,
                                       MemberOffset offset = MemberOffset(0))
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  ALWAYS_INLINE void MarkObject(mirror::Object* obj, mirror::Object* holder, MemberOffset offset)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  template <bool kParallel>
  ALWAYS_INLINE bool MarkObjectNonNullNoPush(mirror::Object* obj,
                                             mirror::Object* holder = nullptr,
                                             MemberOffset offset = MemberOffset(0))
      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_);

  void Sweep(bool swap_bitmaps) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  void SweepLargeObjects(bool swap_bitmaps) REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  // Perform all kernel operations required for concurrent compaction. Includes
  // mremap to move pre-compact pages to from-space, followed by userfaultfd
  // registration on the moving space and linear-alloc.
  void KernelPreparation();
  // Called by KernelPreparation() for every memory range being prepared for
  // userfaultfd registration.
  void KernelPrepareRangeForUffd(uint8_t* to_addr, uint8_t* from_addr, size_t map_size);

  void RegisterUffd(void* addr, size_t size);
  void UnregisterUffd(uint8_t* start, size_t len);

  // Called by SIGBUS handler to compact and copy/map the fault page in moving space.
  void ConcurrentlyProcessMovingPage(uint8_t* fault_page,
                                     uint8_t* buf,
                                     size_t nr_moving_space_used_pages,
                                     bool tolerate_enoent) REQUIRES_SHARED(Locks::mutator_lock_);
  // Called by SIGBUS handler to process and copy/map the fault page in linear-alloc.
  void ConcurrentlyProcessLinearAllocPage(uint8_t* fault_page, bool tolerate_enoent)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Process concurrently all the pages in linear-alloc. Called by gc-thread.
  void ProcessLinearAlloc() REQUIRES_SHARED(Locks::mutator_lock_);

  // Does the following:
  // 1. Checks the status of to-space pages in [cur_page_idx,
  //    last_checked_reclaim_page_idx_) range to see whether the corresponding
  //    from-space pages can be reused.
  // 2. Taking into consideration classes which are allocated after their
  //    objects (in address order), computes the page (in from-space) from which
  //    actual reclamation can be done.
  // 3. Map the pages in [cur_page_idx, end_idx_for_mapping) range.
  // 4. Madvise the pages in [page from (2), last_reclaimed_page_)
  bool FreeFromSpacePages(size_t cur_page_idx, int mode, size_t end_idx_for_mapping)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Maps moving space pages in [start_idx, arr_len) range. It fetches the page
  // address containing the compacted content from moving_pages_status_ array.
  // 'from_fault' is true when called from userfault (sigbus handler).
  // 'return_on_contention' is set to true by gc-thread while it is compacting
  // pages. In the end it calls the function with `return_on_contention=false`
  // to ensure all pages are mapped. Returns number of pages that are mapped.
  size_t MapMovingSpacePages(size_t start_idx,
                             size_t arr_len,
                             bool from_fault,
                             bool return_on_contention,
                             bool tolerate_enoent) REQUIRES_SHARED(Locks::mutator_lock_);

  bool IsValidFd(int fd) const { return fd >= 0; }

  PageState GetPageStateFromWord(uint32_t page_word) {
    return static_cast<PageState>(static_cast<uint8_t>(page_word));
  }

  PageState GetMovingPageState(size_t idx) {
    return GetPageStateFromWord(moving_pages_status_[idx].load(std::memory_order_acquire));
  }

  // Add/update <class, obj> pair if class > obj and obj is the lowest address
  // object of class.
  ALWAYS_INLINE void UpdateClassAfterObjectMap(mirror::Object* obj)
      REQUIRES_SHARED(Locks::mutator_lock_);

  void MarkZygoteLargeObjects() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);

  // Map zero-pages in the given range. 'tolerate_eexist' and 'tolerate_enoent'
  // help us decide if we should expect EEXIST or ENOENT back from the ioctl
  // respectively. It may return after mapping fewer pages than requested.
  // found to be contended, then we delay the operations based on thread's
  // Returns number of bytes (multiple of page-size) now known to be mapped.
  size_t ZeropageIoctl(void* addr, size_t length, bool tolerate_eexist, bool tolerate_enoent);
  // Map 'buffer' to 'dst', both being 'length' bytes using at most one ioctl
  // call. 'return_on_contention' indicates that the function should return
  // as soon as mmap_lock contention is detected. Like ZeropageIoctl(), this
  // function also uses thread's priority to decide how long we delay before
  // forcing the ioctl operation. If ioctl returns EEXIST, then also function
  // returns. Returns number of bytes (multiple of page-size) mapped.
  size_t CopyIoctl(
      void* dst, void* buffer, size_t length, bool return_on_contention, bool tolerate_enoent);
  // Move 'len/page-size' pages from 'src' to 'dst'.
  size_t MoveIoctl(void* dst, void* src, size_t len, bool tolerate_einval);

  // Called after updating linear-alloc page(s) to map the page. It first
  // updates the state of the pages to kProcessedAndMapping and after ioctl to
  // kProcessedAndMapped. Returns true if at least the first page is now mapped.
  // If 'free_pages' is true then also frees shadow pages. If 'single_ioctl'
  // is true, then stops after first ioctl.
  bool MapUpdatedLinearAllocPages(uint8_t* start_page,
                                  uint8_t* start_shadow_page,
                                  Atomic<PageState>* state,
                                  size_t length,
                                  bool free_pages,
                                  bool single_ioctl,
                                  bool tolerate_enoent);
  // Called for clamping of 'info_map_' and other GC data structures, which are
  // small and/or in >4GB address space. There is no real benefit of clamping
  // them synchronously during app forking. It clamps only if clamp_info_map_status_
  // is set to kClampInfoPending, which is done by ClampGrowthLimit().
  void MaybeClampGcStructures() REQUIRES(Locks::heap_bitmap_lock_);

  size_t ComputeInfoMapSize();
  // Initialize all the info-map related fields of this GC. Returns total size
  // of all the structures in info-map.
  size_t InitializeInfoMap(uint8_t* p, size_t moving_space_sz);
  // Update class-table classes in compaction pause if we are running in debuggable
  // mode. Only visit class-table in image spaces if 'immune_class_table_only'
  // is true.
  void UpdateClassTableClasses(Runtime* runtime, bool immune_class_table_only)
      REQUIRES_SHARED(Locks::mutator_lock_);

  void SweepArray(accounting::ObjectStack* obj_arr, bool swap_bitmaps)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);

  // Set bit corresponding to 'obj' in 'mid_to_old_promo_bit_vec_' bit-vector.
  // 'obj' is the post-compacted object in mid-gen, which will get promoted to
  // old-gen and hence 'mid_to_old_promo_bit_vec_' is copied into mark-bitmap at
  // the end of GC for next GC cycle.
  void SetBitForMidToOldPromotion(uint8_t* obj);
  // Scan old-gen for young GCs by looking for cards that are at least 'aged' in
  // the card-table corresponding to moving and non-moving spaces.
  NO_INLINE void ScanOldGenObjects() REQUIRES(Locks::heap_bitmap_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Return free pages from 'from-space' to be reused. Returns nullptr if 'size'
  // worth of contiguous pages are not available. 'size' must be a multiple of
  // page-size.
  uint8_t* GetRecyclablePages(size_t size, bool atomic);

  // Verify that cards corresponding to objects containing references to
  // young-gen are dirty.
  void VerifyNoMissingGenerationalCardMarks()
      REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
  // Verify that card corresponding to a marked object with unmarked reference is dirty.
  void VerifyNoMissingCardMarks() REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
  // Verify that post-GC objects (all objects except the ones allocated after
  // marking pause) are valid with valid references in them. Bitmap corresponding
  // to [moving_space_begin_, mark_bitmap_clear_end) was retained. This is used in
  // case compaction is skipped.
  void VerifyPostGCObjects(bool performed_compaction, uint8_t* mark_bitmap_clear_end)
      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_);

  // Like ProcessMarkStack(), but ignores null entries.
  void ProcessMarkStackNonNull() REQUIRES_SHARED(Locks::mutator_lock_)
      REQUIRES(Locks::heap_bitmap_lock_);
  // Called to assess if it's safe to use MOVE ioctl, both from kernel bug-fixes
  // as well as seccomp filter point of view.
  bool MoveIoctlKernelCheck();

  // Returns class-size of 'klass'. If kHandleZeroReads == true, then it checks
  // for 0 class-size, which can happen if, during compaction, the page
  // containing klass' size gets moved to the to-space and we are reading from a
  // shared zero-page. In that case, we read class-size from 'moved_klass'.
  template <bool kHandleZeroReads, VerifyObjectFlags kVerifyFlags>
  size_t GetClassSize(mirror::Class* klass, mirror::Class* moved_klass)
      REQUIRES_SHARED(Locks::mutator_lock_);
  // Moves the from-space 'page' to to-space. If the move operation is already
  // in-progress by another thread, then it waits until the page's status
  // changes to 'mapped'.
  void MoveBlackDensePageForUpdate(uint8_t* page)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Updates references in the given object-array in [begin, end) range by
  // calling the visitor. Returns length of the array. If kHandleZeroReads ==
  // true, then makes sure it is reading the correct length and not 0 from a
  // shared zero-page because the containing page has been moved to to-space.
  template <bool kHandleZeroReads, VerifyObjectFlags kVerifyFlags, typename Visitor>
  int32_t UpdateObjArrayReferences(mirror::ObjectArray<mirror::Object>* arr,
                                   Visitor& visitor,
                                   MemberOffset begin,
                                   MemberOffset end)
      REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);

  // Updates static references in the given klass by calling visitor on each of
  // them. If kHandleZeroReads == true, then makes sure that the static
  // references' offset and count is not incorrectly read to be 0 from shared
  // zero-page in from space.
  template <bool kHandleZeroReads, VerifyObjectFlags kVerifyFlags, typename Visitor>
  void UpdateStaticFieldsReferences(mirror::Class* klass, Visitor& visitor)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Updates obj's instance references by calling visitor on each of them. Makes
  // sure that if the klass is in black-dense region then it's not incorrectly
  // assuming instance-reference bitmap to be 0 due to shared zero-page.
  template <VerifyObjectFlags kVerifyFlags, typename Visitor>
  void UpdateInstanceFieldsReferences(mirror::Object* obj,
                                      mirror::Class* to_klass,
                                      mirror::Class* klass,
                                      const Visitor& visitor)
      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);

  // Updates references in 'obj' by calling 'visitor' on each reference during
  // compaction. Returns object-size, if kFetchObjSize == true. If
  // kObjInBlackDense == true, then it takes extra precautions when reading
  // fields from 'obj' in case a part of it is on a page which has already moved
  // to to-space.
  template <bool kFetchObjSize,
            bool kObjInBlackDense,
            VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            typename Visitor>
  size_t UpdateRefsForCompaction(mirror::Object* obj,
                                 const Visitor& visitor,
                                 MemberOffset begin,
                                 MemberOffset end)
      REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);

  // If using MOVE ioctl, atomically fetch a free from-space page, clear it,
  // and then move to 'dst'. Returns MoveIoctl()'s return value, or max-val
  // if we couldn't find any available page.
  size_t ZeroAndMoveFreePage(uint8_t* dst, bool tolerate_einval);
  // Vector to hold thread-local overflow arrays (and the number of entries in
  // there) of gc-roots found during mutator-stack scanning in marking phase.
  std::vector<std::pair<StackReference<mirror::Object>*, size_t>>* overflow_arrays_
      GUARDED_BY(lock_);
  // For checkpoints
  Barrier gc_barrier_;
  // Required only when mark-stack is accessed in shared mode, which happens
  // when collecting thread-stack roots using checkpoint. Otherwise, we use it
  // to synchronize on updated_roots_ in debug-builds.
  Mutex lock_;
  // Counters to synchronize mutator threads and gc-thread at the end of
  // compaction. Counter 0 represents the number of mutators still working on
  // moving space pages which started before gc-thread finished compacting pages,
  // whereas the counter 1 represents those which started afterwards but
  // before unregistering the space from uffd. Once counter 1 reaches 0, the
  // gc-thread madvises spaces and data structures like page-status array.
  // Both the counters are set to 0 before compaction begins. They are or'ed
  // with kSigbusCounterCompactionDoneMask one-by-one by gc-thread after
  // compaction to communicate the status to future mutators.
  std::atomic<SigbusCounterType> sigbus_in_progress_count_[2];
  MemMap from_space_map_;
  // Any array of live-bytes in logical chunks of kOffsetChunkSize size
  // in the 'to-be-compacted' space.
  MemMap info_map_;
  // Set of page-sized buffers used for compaction. The first page is used by
  // the GC thread. Subdequent pages are used by mutator threads in case of
  // SIGBUS feature, and by uffd-worker threads otherwise. In the latter case
  // the first page is also used for termination of concurrent compaction by
  // making worker threads terminate the userfaultfd read loop.
  MemMap compaction_buffers_map_;

  class LessByArenaAddr {
   public:
    bool operator()(const TrackedArena* a, const TrackedArena* b) const {
      return std::less<uint8_t*>{}(a->Begin(), b->Begin());
    }
  };

  // Map of arenas allocated in LinearAlloc arena-pool and last non-zero page,
  // captured during compaction pause for concurrent updates.
  std::map<const TrackedArena*, uint8_t*, LessByArenaAddr> linear_alloc_arenas_;
  // Set of PageStatus arrays, one per arena-pool space. It's extremely rare to
  // have more than one, but this is to be ready for the worst case.
  class LinearAllocSpaceData {
   public:
    LinearAllocSpaceData(MemMap&& shadow, MemMap&& page_status_map, uint8_t* begin, uint8_t* end)
        : shadow_(std::move(shadow)),
          page_status_map_(std::move(page_status_map)),
          begin_(begin),
          end_(end) {}

    MemMap shadow_;
    MemMap page_status_map_;
    uint8_t* begin_;
    uint8_t* end_;
  };
  std::vector<LinearAllocSpaceData> linear_alloc_spaces_data_;

  class LessByObjReference {
   public:
    bool operator()(const ObjReference& a, const ObjReference& b) const {
      return std::less<mirror::Object*>{}(a.AsMirrorPtr(), b.AsMirrorPtr());
    }
  };
  using ClassAfterObjectMap = std::map<ObjReference, ObjReference, LessByObjReference>;
  // map of <K, V> such that the class K (in moving space) is after its
  // objects, and its object V is the lowest object (in moving space).
  ClassAfterObjectMap class_after_obj_map_;
  // Since the compaction is done in reverse, we use a reverse iterator. It is maintained
  // either at the pair whose class is lower than the first page to be freed, or at the
  // pair whose object is not yet compacted.
  ClassAfterObjectMap::const_reverse_iterator class_after_obj_iter_;
  // Every object inside the immune spaces is assumed to be marked.
  ImmuneSpaces immune_spaces_;
  // Bit-vector to store bits for objects which are promoted from mid-gen to
  // old-gen during compaction. Later in FinishPhase() it's copied into
  // mark-bitmap of moving-space.
  std::unique_ptr<BitVector> mid_to_old_promo_bit_vec_;

  // List of objects found to have native gc-roots into young-gen during
  // marking. Cards corresponding to these objects are dirtied at the end of GC.
  // These have to be captured during marking phase as we don't update
  // native-roots during compaction.
  std::vector<mirror::Object*> dirty_cards_later_vec_;
  space::ContinuousSpace* non_moving_space_;
  space::BumpPointerSpace* const bump_pointer_space_;
  Thread* thread_running_gc_;
  // Length of 'chunk_info_vec_' vector (defined below).
  size_t vector_length_;
  size_t live_stack_freeze_size_;
  size_t non_moving_first_objs_count_;
  // Length of first_objs_moving_space_ and pre_compact_offset_moving_space_
  // arrays. Also the number of pages which are to be compacted.
  size_t moving_first_objs_count_;
  // Number of pages containing black-allocated objects, indicating number of
  // pages to be slid.
  size_t black_page_count_;
  // Used by FreeFromSpacePages() for maintaining markers in the moving space for
  // how far the pages have been reclaimed (madvised) and checked.
  //
  // Pages from this index to the end of to-space have been checked (via page_status)
  // and their corresponding from-space pages are reclaimable.
  size_t last_checked_reclaim_page_idx_;
  // All from-space pages in [last_reclaimed_page_, from_space->End()) are
  // reclaimed (madvised). Pages in [from-space page corresponding to
  // last_checked_reclaim_page_idx_, last_reclaimed_page_) are not reclaimed as
  // they may contain classes required for class hierarchy traversal for
  // visiting references during compaction.
  uint8_t* last_reclaimed_page_;
  // All the pages in [last_reclaimable_page_, last_reclaimed_page_) in
  // from-space are available to store compacted contents for batching until the
  // next time madvise is called.
  // Declared atomic as gc-thread may write to it while mutators are accessing
  // it concurrently.
  std::atomic<uint8_t*> last_reclaimable_page_;
  // [cur_reclaimable_page_, last_reclaimed_page_) have been used to store
  // compacted contents for batching.
  std::atomic<uint8_t*> cur_reclaimable_page_;

  // Mark bits for non-moving space
  accounting::ContinuousSpaceBitmap* non_moving_space_bitmap_;
  // Mark bits for large-object space
  accounting::LargeObjectBitmap* large_object_space_bitmap_;
  // Array of moving-space's pages' compaction status, which is stored in the
  // least-significant byte. kProcessed entries also contain the from-space
  // offset of the page which contains the compacted contents of the ith
  // to-space page.
  Atomic<uint32_t>* moving_pages_status_;
  // For pages before black allocations, moving_space_pages_info_[i] holds
  // offset within the space from where the objects need to be copied in the ith
  // post-compact page.
  // Otherwise, moving_space_pages_info_[i] holds the size of first non-empty
  // chunk in the ith black-allocations page.
  // This array is live during compaction and gets initialized in
  // PrepareFroCompaction(). Prior to that we may use the array in the full-heap
  // GC case in PrepareForCompaction() for temporarily storing live-bytes of
  // every moving space page.
  uint32_t* moving_space_pages_info_;
  // first_objs_moving_space_[i] is the pre-compact address of the object which
  // would overlap with the starting boundary of the ith post-compact page.
  ObjReference* first_objs_moving_space_;
  // First object for every page. It could be greater than the page's start
  // address, or null if the page is empty.
  ObjReference* first_objs_non_moving_space_;
  uint8_t* from_space_begin_;

  // The moving space markers are ordered as follows:
  // [moving_space_begin_, black_dense_end_, mid_gen_end_, post_compact_end_, moving_space_end_)

  // End of compacted space. Used for computing post-compact address of black
  // allocated objects. Aligned up to page size.
  uint8_t* post_compact_end_;

  // BEGIN HOT FIELDS: accessed per object

  accounting::ObjectStack* mark_stack_;
  uint64_t bytes_scanned_;
  // Number of objects freed during this GC in moving space. It is decremented
  // every time an object is discovered. And total-object count is added to it
  // in MarkingPause(). It reaches the correct count only once the marking phase
  // is completed.
  int32_t freed_objects_;
  // Set to true when doing young gen collection.
  bool young_gen_;
  const bool use_generational_;
  bool use_move_ioctl_;
  // True while compacting.
  bool compacting_;
  // Mark bits for main space
  accounting::ContinuousSpaceBitmap* const moving_space_bitmap_;
  // Cache (from_space_begin_ - bump_pointer_space_->Begin()) so that we can
  // compute from-space address of a given pre-comapct address efficiently.
  ptrdiff_t from_space_slide_diff_;
  // Cached values of moving-space range to optimize checking if reference
  // belongs to moving-space or not. May get updated if and when heap is clamped.
  uint8_t* const moving_space_begin_;
  uint8_t* moving_space_end_;
  // In generational-mode, we maintain 3 generations: young, mid, and old.
  // Mid generation is collected during young collections. This means objects
  // need to survive two GCs before they get promoted to old-gen. This helps
  // in avoiding pre-mature promotion of objects which are allocated just
  // prior to a young collection but are short-lived.

  // Set to moving_space_begin_ if compacting the entire moving space.
  // Otherwise, set to a page-aligned address such that [moving_space_begin_,
  // black_dense_end_) is considered to be densely populated with reachable
  // objects and hence is not compacted. In generational mode, old-gen is
  // treated just like black-dense region.
  union {
    uint8_t* black_dense_end_;
    uint8_t* old_gen_end_;
  };
  // Prior to compaction, 'mid_gen_end_' represents end of 'pre-compacted'
  // mid-gen. During compaction, it represents 'post-compacted' end of mid-gen.
  // This is done in PrepareForCompaction(). At the end of GC, in FinishPhase(),
  // mid-gen gets consumed/promoted to old-gen, and young-gen becomes mid-gen,
  // in preparation for the next GC cycle.
  uint8_t* mid_gen_end_;

  // BEGIN HOT FIELDS: accessed per reference update

  // Special bitmap wherein all the bits corresponding to an object are set.
  // TODO: make LiveWordsBitmap encapsulated in this class rather than a
  // pointer. We tend to access its members in performance-sensitive
  // code-path. Also, use a single MemMap for all the GC's data structures,
  // which we will clear in the end. This would help in limiting the number of
  // VMAs that get created in the kernel.
  std::unique_ptr<LiveWordsBitmap<kAlignment>> live_words_bitmap_;
  // For every page in the to-space (post-compact heap) we need to know the
  // first object from which we must compact and/or update references. This is
  // for both non-moving and moving space. Additionally, for the moving-space,
  // we also need the offset within the object from where we need to start
  // copying.
  // chunk_info_vec_ holds live bytes for chunks during marking phase. After
  // marking we perform an exclusive scan to compute offset for every chunk.
  uint32_t* chunk_info_vec_;
  // moving-space's end pointer at the marking pause. All allocations beyond
  // this will be considered black in the current GC cycle. Aligned up to page
  // size.
  uint8_t* black_allocations_begin_;
  // Cache (black_allocations_begin_ - post_compact_end_) for post-compact
  // address computations.
  ptrdiff_t black_objs_slide_diff_;

  // END HOT FIELDS: accessed per reference update
  // END HOT FIELDS: accessed per object

  PointerSize pointer_size_;
  // Userfault file descriptor, accessed only by the GC itself.
  // kFallbackMode value indicates that we are in the fallback mode.
  int uffd_;
  // When using SIGBUS feature, this counter is used by mutators to claim a page
  // out of compaction buffers to be used for the entire compaction cycle.
  std::atomic<uint16_t> compaction_buffer_counter_;
  // Set to true in MarkingPause() to indicate when allocation_stack_ should be
  // checked in IsMarked() for black allocations.
  bool marking_done_;
  // Indicates if the concurrent compaction has started or not. Only accessed by
  // the GC thread.
  bool conc_compaction_started_;
  // Flag indicating whether one-time uffd initialization has been done. It will
  // be false on the first GC for non-zygote processes, and always for zygote.
  // Its purpose is to minimize the userfaultfd overhead to the minimal in
  // Heap::PostForkChildAction() as it's invoked in app startup path. With
  // this, we register the compaction-termination page on the first GC.
  bool uffd_initialized_;
  // Clamping statue of `info_map_`. Initialized with 'NotDone'. Once heap is
  // clamped but info_map_ is delayed, we set it to 'Pending'. Once 'info_map_'
  // is also clamped, then we set it to 'Finished'.
  ClampInfoStatus clamp_info_map_status_;

  // Track GC-roots updated so far in a GC-cycle. This is to confirm that no
  // GC-root is updated twice.
  // TODO: Must be replaced with an efficient mechanism eventually. Or ensure
  // that double updation doesn't happen in the first place.
  std::unique_ptr<std::unordered_set<void*>> updated_roots_ GUARDED_BY(lock_);
  // Following values for logging purposes
  void* prev_post_compact_end_;
  void* prev_black_dense_end_;
  void* prev_black_allocations_begin_;
  void* prev_moving_space_end_at_compaction_;
  bool prev_gc_young_;
  bool prev_gc_performed_compaction_;
  // Timestamp when the read-barrier is enabled
  uint64_t app_slow_path_start_time_;

  class FlipCallback;
  class ThreadFlipVisitor;
  class VerifyRootMarkedVisitor;
  class ScanObjectVisitor;
  class CheckpointMarkThreadRoots;
  template <size_t kBufferSize>
  class ThreadRootsVisitor;
  template <bool kCheckBegin, bool kCheckEnd, bool kDirtyOldToMid = false>
  class RefsUpdateVisitor;
  class ArenaPoolPageUpdater;
  class ClassLoaderRootsUpdater;
  class LinearAllocPageUpdater;
  class ImmuneSpaceUpdateObjVisitor;
  template <typename Visitor>
  class VisitReferencesVisitor;

  DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact);
};

std::ostream& operator<<(std::ostream& os, MarkCompact::PageState value);
std::ostream& operator<<(std::ostream& os, MarkCompact::ClampInfoStatus value);

}  // namespace collector
}  // namespace gc
}  // namespace art

#endif  // ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_

Messung V0.5 in Prozent
C=89 H=93 G=90

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik