namespace accounting { class ReadBarrierTable;
} // namespace accounting
namespace space {
// Cyclic region allocation strategy. If `true`, region allocation // will not try to allocate a new region from the beginning of the // region space, but from the last allocated region. This allocation // strategy reduces region reuse and should help catch some GC bugs // earlier. However, cyclic region allocation can also create memory // fragmentation at the region level (see b/33795328); therefore, we // only enable it in debug mode. static constexpr bool kCyclicRegionAllocation = kIsDebugBuild;
// A space that consists of equal-sized regions. class RegionSpace final : public ContinuousMemMapAllocSpace { public: using WalkCallback = void (*)(void *start, void *end, size_t num_bytes, void* callback_arg);
// Create a region space mem map with the requested sizes. The requested base address is not // guaranteed to be granted, if it is required, the caller should call Begin on the returned // space to confirm the request was granted. static MemMap CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin); static RegionSpace* Create(const std::string& name, MemMap&& mem_map, bool use_generational_cc);
// Allocate `num_bytes`, returns null if the space is full.
mirror::Object* Alloc(Thread* self,
size_t num_bytes, /* out */ size_t* bytes_allocated, /* out */ size_t* usable_size, /* out */ size_t* bytes_tl_bulk_allocated)
override REQUIRES(!region_lock_); // Thread-unsafe allocation for when mutators are suspended, used by the semispace collector.
mirror::Object* AllocThreadUnsafe(Thread* self,
size_t num_bytes, /* out */ size_t* bytes_allocated, /* out */ size_t* usable_size, /* out */ size_t* bytes_tl_bulk_allocated)
override REQUIRES(Locks::mutator_lock_) REQUIRES(!region_lock_); // The main allocation routine. template<bool kForEvac>
ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, /* out */ size_t* bytes_allocated, /* out */ size_t* usable_size, /* out */ size_t* bytes_tl_bulk_allocated)
REQUIRES(!region_lock_); // Allocate/free large objects (objects that are larger than the region size). template<bool kForEvac>
mirror::Object* AllocLarge(size_t num_bytes, /* out */ size_t* bytes_allocated, /* out */ size_t* usable_size, /* out */ size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_); template<bool kForEvac> void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) REQUIRES(!region_lock_);
// Return the storage space required by obj.
size_t AllocationSize(mirror::Object* obj, size_t* usable_size) override
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!region_lock_) { return AllocationSizeNonvirtual(obj, usable_size);
}
EXPORT size_t AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!region_lock_);
// Remove read and write memory protection from the whole region space, // i.e. make memory pages backing the region area not readable and not // writable. void Protect();
// Remove memory protection from the whole region space, i.e. make memory // pages backing the region area readable and writable. This method is useful // to avoid page protection faults when dumping information about an invalid // reference.
EXPORT void Unprotect();
// Change the non growth limit capacity to new capacity by shrinking or expanding the map. // Currently, only shrinking is supported. // Unlike implementations of this function in other spaces, we need to pass // new capacity as argument here as region space doesn't have any notion of // growth limit. void ClampGrowthLimit(size_t new_capacity) REQUIRES(!region_lock_);
EXPORT void Dump(std::ostream& os) const override; void DumpRegions(std::ostream& os) REQUIRES(!region_lock_); // Dump region containing object `obj`. Precondition: `obj` is in the region space. void DumpRegionForObject(std::ostream& os, mirror::Object* obj) REQUIRES(!region_lock_);
EXPORT void DumpNonFreeRegions(std::ostream& os) REQUIRES(!region_lock_);
enumclass RegionType : uint8_t {
kRegionTypeAll, // All types.
kRegionTypeFromSpace, // From-space. To be evacuated.
kRegionTypeUnevacFromSpace, // Unevacuated from-space. Not to be evacuated.
kRegionTypeToSpace, // To-space.
kRegionTypeNone, // None.
};
enumclass RegionState : uint8_t {
kRegionStateFree, // Free region.
kRegionStateAllocated, // Allocated region.
kRegionStateLarge, // Large allocated (allocation larger than the region size).
kRegionStateLargeTail, // Large tail (non-first regions of a large allocation).
};
// Go through all of the blocks and visit the continuous objects. template <typename Visitor>
ALWAYS_INLINE void Walk(Visitor&& visitor) REQUIRES(Locks::mutator_lock_); template <typename Visitor>
ALWAYS_INLINE void WalkToSpace(Visitor&& visitor) REQUIRES(Locks::mutator_lock_);
// Scans regions and calls visitor for objects in unevac-space corresponding // to the bits set in 'bitmap'. // Cannot acquire region_lock_ as visitor may need to acquire it for allocation. // Should not be called concurrently with functions (like SetFromSpace()) which // change regions' type. template <typename Visitor>
ALWAYS_INLINE void ScanUnevacFromSpace(accounting::ContinuousSpaceBitmap* bitmap,
Visitor&& visitor) NO_THREAD_SAFETY_ANALYSIS;
bool IsInNewlyAllocatedRegion(mirror::Object* ref) { if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref); return r->IsNewlyAllocated();
} returnfalse;
}
bool IsInUnevacFromSpace(mirror::Object* ref) { if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref); return r->IsInUnevacFromSpace();
} returnfalse;
}
bool IsLargeObject(mirror::Object* ref) { if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref); return r->IsLarge();
} returnfalse;
}
bool IsInToSpace(mirror::Object* ref) { if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref); return r->IsInToSpace();
} returnfalse;
}
// If `ref` is in the region space, return the type of its region; // otherwise, return `RegionType::kRegionTypeNone`.
RegionType GetRegionType(mirror::Object* ref) { if (HasAddress(ref)) { return GetRegionTypeUnsafe(ref);
} return RegionType::kRegionTypeNone;
}
// Unsafe version of RegionSpace::GetRegionType. // Precondition: `ref` is in the region space.
RegionType GetRegionTypeUnsafe(mirror::Object* ref) {
DCHECK(HasAddress(ref)) << ref;
Region* r = RefToRegionUnlocked(ref); return r->Type();
}
// Zero live bytes for a large object, used by young gen CC for marking newly allocated large // objects. void ZeroLiveBytesForLargeObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
// Determine which regions to evacuate and tag them as // from-space. Tag the rest as unevacuated from-space. void SetFromSpace(accounting::ReadBarrierTable* rb_table,
EvacMode evac_mode, bool clear_live_bytes)
REQUIRES(!region_lock_);
void AssertAllRegionLiveBytesZeroOrCleared() REQUIRES(!region_lock_) { if (kIsDebugBuild) {
MutexLock mu(Thread::Current(), region_lock_); for (size_t i = 0; i < num_regions_; ++i) {
Region* r = ®ions_[i];
size_t live_bytes = r->LiveBytes();
CHECK(live_bytes == 0U || live_bytes == static_cast<size_t>(-1)) << live_bytes;
}
}
}
void SetAllRegionLiveBytesZero() REQUIRES(!region_lock_) {
MutexLock mu(Thread::Current(), region_lock_); const size_t iter_limit = kUseTableLookupReadBarrier
? num_regions_
: std::min(num_regions_, non_free_region_index_limit_); for (size_t i = 0; i < iter_limit; ++i) {
Region* r = ®ions_[i]; // Newly allocated regions don't need up-to-date live_bytes_ for deciding // whether to be evacuated or not. See Region::ShouldBeEvacuated(). if (!r->IsFree() && !r->IsNewlyAllocated()) {
r->ZeroLiveBytes();
}
}
}
size_t RegionIdxForRefUnchecked(mirror::Object* ref) const NO_THREAD_SAFETY_ANALYSIS {
DCHECK(HasAddress(ref));
uintptr_t offset = reinterpret_cast<uintptr_t>(ref) - reinterpret_cast<uintptr_t>(Begin());
size_t reg_idx = offset / kRegionSize;
DCHECK_LT(reg_idx, num_regions_);
Region* reg = ®ions_[reg_idx];
DCHECK_EQ(reg->Idx(), reg_idx);
DCHECK(reg->Contains(ref)); return reg_idx;
} // Return -1 as region index for references outside this region space.
size_t RegionIdxForRef(mirror::Object* ref) const NO_THREAD_SAFETY_ANALYSIS { if (HasAddress(ref)) { return RegionIdxForRefUnchecked(ref);
} else { returnstatic_cast<size_t>(-1);
}
}
// Increment object allocation count for region containing ref. void RecordAlloc(mirror::Object* ref) REQUIRES(!region_lock_);
// Given a free region, declare it non-free (allocated). void Unfree(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
// Given a free region, declare it non-free (allocated) and large.
EXPORT void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
// Given a free region, declare it non-free (allocated) and large tail.
EXPORT void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
// Set this region as evacuated from-space. At the end of the // collection, RegionSpace::ClearFromSpace will clear and reclaim // the space used by this region, and tag it as unallocated/free. void SetAsFromSpace() {
DCHECK(!IsFree() && IsInToSpace());
type_ = RegionType::kRegionTypeFromSpace; if (IsNewlyAllocated()) { // Clear the "newly allocated" status here, as we do not want the // GC to see it when encountering references in the from-space. // // Invariant: There should be no newly-allocated region in the // from-space (when the from-space exists, which is between the calls // to RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace).
is_newly_allocated_ = false;
} // Set live bytes to an invalid value, as we have made an // evacuation decision (possibly based on the percentage of live // bytes).
live_bytes_ = static_cast<size_t>(-1);
}
// Set this region as unevacuated from-space. At the end of the // collection, RegionSpace::ClearFromSpace will preserve the space // used by this region, and tag it as to-space (see // Region::SetUnevacFromSpaceAsToSpace below). void SetAsUnevacFromSpace(bool clear_live_bytes);
// Set this region as to-space. Used by RegionSpace::ClearFromSpace. // This is only valid if it is currently an unevac from-space region. void SetUnevacFromSpaceAsToSpace() {
DCHECK(!IsFree() && IsInUnevacFromSpace());
type_ = RegionType::kRegionTypeToSpace;
}
// Return whether this region should be evacuated. Used by RegionSpace::SetFromSpace.
ALWAYS_INLINE bool ShouldBeEvacuated(EvacMode evac_mode);
void AddLiveBytes(size_t live_bytes) {
DCHECK(GetUseGenerationalCC() || IsInUnevacFromSpace());
DCHECK(!IsLargeTail());
DCHECK_NE(live_bytes_, static_cast<size_t>(-1)); // For large allocations, we always consider all bytes in the regions live.
live_bytes_ += IsLarge() ? Top() - begin_ : live_bytes;
DCHECK_LE(live_bytes_, BytesAllocated());
}
size_t idx_; // The region's index in the region space. // Number of bytes in live objects, or -1 for newly allocated regions. Used to compute // percent live for region evacuation decisions, and to determine whether an unevacuated // region is completely empty, and thus can be reclaimed. Reset to zero either at the // beginning of MarkingPhase(), or during the flip for a nongenerational GC, where we // don't have a separate mark phase. It is then incremented whenever a mark bit in that // region is set.
size_t live_bytes_; // The live bytes. Used to compute the live percent.
uint8_t* begin_; // The begin address of the region.
Thread* thread_; // The owning thread if it's a tlab. // Note that `top_` can be higher than `end_` in the case of a // large region, where an allocated object spans multiple regions // (large region + one or more large tail regions).
Atomic<uint8_t*> top_; // The current position of the allocation.
uint8_t* end_; // The end address of the region. // objects_allocated_ is accessed using memory_order_relaxed. Treat as approximate when there // are concurrent updates.
Atomic<size_t> objects_allocated_; // The number of objects allocated.
uint32_t alloc_time_; // The allocation time of the region. // Note that newly allocated and evacuated regions use -1 as // special value for `live_bytes_`. bool is_newly_allocated_; // True if it's allocated after the last collection. bool is_a_tlab_; // True if it's a tlab.
RegionState state_; // The region state (see RegionState).
RegionType type_; // The region type (see RegionType).
Region* RefToRegionUnlocked(mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS { // For a performance reason (this is frequently called via // RegionSpace::IsInFromSpace, etc.) we avoid taking a lock here. // Note that since we only change a region from to-space to (evac) // from-space during a pause (in RegionSpace::SetFromSpace) and // from (evac) from-space to free (after GC is done), as long as // `ref` is a valid reference into an allocated region, it's safe // to access the region state without the lock. return RefToRegionLocked(ref);
}
// Return the object location following `obj` in the region space // (i.e., the object location at `obj + obj->SizeOf()`). // // Note that unless // - the region containing `obj` is fully used; and // - `obj` is not the last object of that region; // the returned location is not guaranteed to be a valid object. static mirror::Object* GetNextObject(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_);
// Implementation of this invariant: // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true. void VerifyNonFreeRegionLimit() REQUIRES(region_lock_) { if (kIsDebugBuild && non_free_region_index_limit_ < num_regions_) { for (size_t i = non_free_region_index_limit_; i < num_regions_; ++i) {
CHECK(regions_[i].IsFree());
}
}
}
// Scan region range [`begin`, `end`) in increasing order to try to // allocate a large region having a size of `num_regs_in_large_region` // regions. If there is no space in the region space to allocate this // large region, return null. // // If argument `next_region` is not null, use `*next_region` to // return the index to the region next to the allocated large region // returned by this method. template<bool kForEvac>
mirror::Object* AllocLargeInRange(size_t begin,
size_t end,
size_t num_regs_in_large_region, /* out */ size_t* bytes_allocated, /* out */ size_t* usable_size, /* out */ size_t* bytes_tl_bulk_allocated, /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_);
// Check that the value of `r->LiveBytes()` matches the number of // (allocated) bytes used by live objects according to the live bits // in the region space bitmap range corresponding to region `r`. void CheckLiveBytesAgainstRegionBitmap(Region* r);
// Poison memory areas used by dead objects within unevacuated // region `r`. This is meant to detect dangling references to dead // objects earlier in debug mode. void PoisonDeadObjectsInUnevacuatedRegion(Region* r);
Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// Cached version of Heap::use_generational_cc_. constbool use_generational_cc_;
uint32_t time_; // The time as the number of collections since the startup.
size_t num_regions_; // The number of regions in this space.
uint64_t madvise_time_; // The amount of time spent in madvise for purging pages. // The number of non-free regions in this space.
size_t num_non_free_regions_ GUARDED_BY(region_lock_);
// The number of evac regions allocated during collection. 0 when GC not running.
size_t num_evac_regions_ GUARDED_BY(region_lock_);
// Maintain the maximum of number of non-free regions collected just before // reclaim in each GC cycle. At this moment in cycle, highest number of // regions are in non-free.
size_t max_peak_num_non_free_regions_;
// The pointer to the region array.
std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_);
// To hold partially used TLABs which can be reassigned to threads later for // utilizing the un-used portion.
std::multimap<size_t, Region*, std::greater<size_t>> partial_tlabs_ GUARDED_BY(region_lock_); // The upper-bound index of the non-free regions. Used to avoid scanning all regions in // RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace. // // Invariant (verified by RegionSpace::VerifyNonFreeRegionLimit): // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true.
size_t non_free_region_index_limit_ GUARDED_BY(region_lock_);
Region* current_region_; // The region currently used for allocation.
Region* evac_region_; // The region currently used for evacuation.
Region full_region_; // The fake/sentinel region that looks full.
// Index into the region array pointing to the starting region when // trying to allocate a new region. Only used when // `kCyclicRegionAllocation` is true.
size_t cyclic_alloc_region_index_ GUARDED_BY(region_lock_);
// Mark bitmap used by the GC.
accounting::ContinuousSpaceBitmap mark_bitmap_;
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.