#ifndef V8_HEAP_HEAP_H_
#define V8_HEAP_HEAP_H_
#include <atomic>
#include <cmath>
#include <memory>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "include/v8-callbacks.h"
#include "include/v8-embedder-heap.h"
#include "include/v8-internal.h"
#include "include/v8-isolate.h"
#include "src/base/atomic-utils.h"
#include "src/base/bounded-page-allocator.h"
#include "src/base/enum-set.h"
#include "src/base/macros.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h"
#include "src/base/small-vector.h"
#include "src/base/strong-alias.h"
#include "src/builtins/accessors.h"
#include "src/common/assert-scope.h"
#include "src/common/code-memory-access.h"
#include "src/common/globals.h"
#include "src/heap/allocation-observer.h"
#include "src/heap/allocation-result.h"
#include "src/heap/base/bytes.h"
#include "src/heap/gc-callbacks.h"
#include "src/heap/heap-allocator.h"
#include "src/heap/marking-state.h"
#include "src/heap/minor-gc-job.h"
#include "src/heap/pretenuring-handler.h"
#include "src/heap/sweeper.h"
#include "src/init/heap-symbols.h"
#include "src/objects/allocation-site.h"
#include "src/objects/fixed-array.h"
#include "src/objects/hash-table.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-array-buffer.h"
#include "src/objects/objects.h"
#include "src/objects/smi.h"
#include "src/objects/visitors.h"
#include "src/roots/roots.h"
#include "src/sandbox/code-pointer-table.h"
#include "src/sandbox/external-pointer-table.h"
#include "src/sandbox/js-dispatch-table.h"
#include "src/sandbox/trusted-pointer-table.h"
#include "src/utils/allocation.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
namespace cppgc::internal {
enum class HeapObjectNameForUnnamedObject : uint8_t;
class ClassNameAsHeapObjectNameScope;
}
namespace heap::base {
class Stack;
class StackVisitor;
}
namespace v8 {
namespace debug {
using OutOfMemoryCallback = void (*)(void* data);
}
namespace internal {
namespace heap {
class HeapTester;
class TestMemoryAllocatorScope;
}
class ArrayBufferCollector;
class ArrayBufferSweeper;
class BackingStore;
class MemoryChunkMetadata;
class Boolean;
class CodeLargeObjectSpace;
class CodeRange;
class CollectionBarrier;
class ConcurrentMarking;
class CppHeap;
class EphemeronRememberedSet;
class GCTracer;
class IncrementalMarking;
class IsolateSafepoint;
class HeapObjectAllocationTracker;
class HeapObjectsFilter;
class HeapProfiler;
class HeapStats;
class Isolate;
class JSArrayBuffer;
class JSFinalizationRegistry;
class JSPromise;
class LinearAllocationArea;
class LocalHeap;
class MemoryAllocator;
class MemoryBalancer;
class MutablePageMetadata;
class MemoryMeasurement;
class MemoryReducer;
class MinorMarkSweepCollector;
class NativeContext;
class NopRwxMemoryWriteScope;
class ObjectIterator;
class ObjectStats;
class PageMetadata;
class PagedSpace;
class PagedNewSpace;
class ReadOnlyHeap;
class RootVisitor;
class RwxMemoryWriteScope;
class SafepointScope;
class Scavenger;
class ScavengerCollector;
class SemiSpaceNewSpace;
class SharedLargeObjectSpace;
class SharedReadOnlySpace;
class SharedSpace;
class SharedTrustedLargeObjectSpace;
class SharedTrustedSpace;
class Space;
class StickySpace;
class StressScavengeObserver;
class TimedHistogram;
class TrustedLargeObjectSpace;
class TrustedRange;
class TrustedSpace;
class WeakObjectRetainer;
enum class ClearRecordedSlots { kYes, kNo };
enum class InvalidateRecordedSlots { kYes, kNo };
enum class InvalidateExternalPointerSlots { kYes, kNo };
enum class ClearFreedMemoryMode { kClearFreedMemory, kDontClearFreedMemory };
enum class SkipRoot {
kExternalStringTable,
kGlobalHandles,
kTracedHandles,
kOldGeneration,
kStack,
kMainThreadHandles,
kUnserializable,
kWeak,
kConservativeStack,
kReadOnlyBuiltins,
};
enum class EmbedderStackStateOrigin {
kImplicitThroughTask,
kExplicitInvocation,
};
class StrongRootsEntry final {
explicit StrongRootsEntry(const char* label) : label(label) {}
const char* label;
FullObjectSlot start;
FullObjectSlot end;
StrongRootsEntry* prev;
StrongRootsEntry* next;
friend class Heap;
};
template <typename T>
using UnorderedHeapObjectMap =
std::unordered_map<Tagged<HeapObject>, T, Object::Hasher,
Object::KeyEqualSafe>;
enum class GCFlag : uint8_t {
kNoFlags = 0,
kReduceMemoryFootprint = 1 << 0,
kForced = 1 << 1,
kLastResort = 1 << 2,
};
class V8_EXPORT_PRIVATE ConservativePinningScope {
V8_STACK_ALLOCATED();
public:
explicit ConservativePinningScope(Heap* heap);
~ConservativePinningScope();
private:
Heap* const heap_;
};
using GCFlags = base::Flags<GCFlag, uint8_t>;
DEFINE_OPERATORS_FOR_FLAGS(GCFlags)
enum class CompleteSweepingReason {
kCollectCodeStatistics,
kHeapObjectIterator,
kStartMarking,
kMinorGC,
kMajorGC,
kHeapSnapshot,
kTearDown,
kFreeze,
kTesting,
kReadOnly,
};
constexpr const char* ToString(CompleteSweepingReason reason) {
switch (reason) {
case CompleteSweepingReason::kCollectCodeStatistics:
return "collect code statistics";
case CompleteSweepingReason::kHeapObjectIterator:
return "heap object iterator";
case CompleteSweepingReason::kStartMarking:
return "start marking";
case CompleteSweepingReason::kMinorGC:
return "minor gc";
case CompleteSweepingReason::kMajorGC:
return "major gc";
case CompleteSweepingReason::kHeapSnapshot:
return "heap snapshot";
case CompleteSweepingReason::kTearDown:
return "tear down";
case CompleteSweepingReason::kFreeze:
return "freeze";
case CompleteSweepingReason::kTesting:
return "testing";
case CompleteSweepingReason::kReadOnly:
return "read only";
}
}
static constexpr v8::base::TimeDelta kMaxSynchronuousGCOperation =
v8::base::TimeDelta::FromMilliseconds(5);
class Heap final {
public:
enum class HeapGrowingMode { kSlow, kConservative, kMinimal, kDefault };
enum HeapState {
NOT_IN_GC,
SCAVENGE,
MARK_COMPACT,
MINOR_MARK_SWEEP,
TEAR_DOWN
};
class V8_NODISCARD DevToolsTraceEventScope {
public:
DevToolsTraceEventScope(Heap* heap, const char* event_name,
const char* event_type);
~DevToolsTraceEventScope();
private:
Heap* heap_;
const char* event_name_;
};
class ExternalMemoryAccounting {
public:
static constexpr size_t kExternalAllocationLimitForInterrupt = 128 * KB;
uint64_t total() const { return total_.load(std::memory_order_relaxed); }
uint64_t limit_for_interrupt() const {
return limit_for_interrupt_.load(std::memory_order_relaxed);
}
uint64_t soft_limit() const {
return low_since_mark_compact() + kExternalAllocationSoftLimit;
}
uint64_t low_since_mark_compact() const {
return low_since_mark_compact_.load(std::memory_order_relaxed);
}
uint64_t UpdateAmount(int64_t delta) {
const uint64_t amount_before =
total_.fetch_add(delta, std::memory_order_relaxed);
CHECK_GE(static_cast<int64_t>(amount_before), -delta);
return amount_before + delta;
}
void UpdateLimitForInterrupt(uint64_t amount) {
set_limit_for_interrupt(amount + kExternalAllocationLimitForInterrupt);
}
void UpdateLowSinceMarkCompact(uint64_t amount) {
set_low_since_mark_compact(amount);
UpdateLimitForInterrupt(amount);
}
uint64_t AllocatedSinceMarkCompact() const {
uint64_t total_bytes = total();
uint64_t low_since_mark_compact_bytes = low_since_mark_compact();
if (total_bytes <= low_since_mark_compact_bytes) {
return 0;
}
return total_bytes - low_since_mark_compact_bytes;
}
private:
void set_total(uint64_t value) {
total_.store(value, std::memory_order_relaxed);
}
void set_limit_for_interrupt(uint64_t value) {
limit_for_interrupt_.store(value, std::memory_order_relaxed);
}
void set_low_since_mark_compact(uint64_t value) {
low_since_mark_compact_.store(value, std::memory_order_relaxed);
}
std::atomic<uint64_t> total_{0};
std::atomic<uint64_t> limit_for_interrupt_{
kExternalAllocationLimitForInterrupt};
std::atomic<uint64_t> low_since_mark_compact_{0};
};
struct LimitBounds {
size_t minimum_old_generation_allocation_limit = 0;
size_t maximum_old_generation_allocation_limit = SIZE_MAX;
size_t minimum_global_allocation_limit = 0;
size_t maximum_global_allocation_limit = SIZE_MAX;
constexpr size_t bounded_old_generation_allocation_limit(size_t val) const {
DCHECK_LE(minimum_old_generation_allocation_limit,
maximum_old_generation_allocation_limit);
return std::clamp(val, minimum_old_generation_allocation_limit,
maximum_old_generation_allocation_limit);
}
constexpr size_t bounded_global_allocation_limit(size_t val) const {
DCHECK_LE(minimum_global_allocation_limit,
maximum_global_allocation_limit);
return std::clamp(val, minimum_global_allocation_limit,
maximum_global_allocation_limit);
}
void AtLeast(size_t new_min_old_gen_limit, size_t new_min_global_limit) {
minimum_old_generation_allocation_limit =
bounded_old_generation_allocation_limit(new_min_old_gen_limit);
minimum_global_allocation_limit =
bounded_global_allocation_limit(new_min_global_limit);
}
void AtMost(size_t new_max_old_gen_limit, size_t new_max_global_limit) {
maximum_old_generation_allocation_limit =
bounded_old_generation_allocation_limit(new_max_old_gen_limit);
maximum_global_allocation_limit =
bounded_global_allocation_limit(new_max_global_limit);
}
static LimitBounds AtLeastCurrentLimits(Heap* heap) {
return {
.minimum_old_generation_allocation_limit =
heap->old_generation_allocation_limit(),
.minimum_global_allocation_limit = heap->global_allocation_limit()};
}
static LimitBounds AtMostCurrentLimits(Heap* heap) {
return {
.maximum_old_generation_allocation_limit =
heap->old_generation_allocation_limit(),
.maximum_global_allocation_limit = heap->global_allocation_limit()};
}
};
struct Chunk {
uint32_t size;
Address start;
Address end;
};
using Reservation = std::vector<Chunk>;
static constexpr size_t kPhysicalMemoryToOldGenerationRatio = 4;
static constexpr size_t kNewLargeObjectSpaceToSemiSpaceRatio = 1;
static const int kTraceRingBufferSize = 512;
static const int kStacktraceBufferSize = 512;
static const int kMinObjectSizeInTaggedWords = 2;
V8_EXPORT_PRIVATE static size_t DefaultInitialOldGenerationSize(
uint64_t physical_memory);
V8_EXPORT_PRIVATE static size_t OldGenerationLowMemory(
uint64_t physical_memory);
#if V8_OS_ANDROID
V8_EXPORT_PRIVATE static bool IsHighEndAndroid(uint64_t physical_memory);
#endif
V8_EXPORT_PRIVATE static size_t HeapLimitMultiplier(uint64_t physical_memory);
static size_t DefaultMinSemiSpaceSize();
V8_EXPORT_PRIVATE static size_t DefaultMaxSemiSpaceSize(
uint64_t physical_memory);
static size_t HeapSizeToSemiSpaceRatio(uint64_t physical_memory);
V8_EXPORT_PRIVATE static size_t DefaultMinHeapSize(uint64_t physical_memory);
V8_EXPORT_PRIVATE static size_t DefaultMaxHeapSize(uint64_t physical_memory);
V8_EXPORT_PRIVATE static int GetMaximumFillToAlign(
AllocationAlignment alignment);
V8_EXPORT_PRIVATE static int GetFillToAlign(Address address,
AllocationAlignment alignment);
static size_t GetCodeRangeReservedAreaSize();
[[noreturn]] V8_EXPORT_PRIVATE void FatalProcessOutOfMemory(
const char* location);
static constexpr std::optional<AllocationSpace>
TryGetAllocationSpaceFromIndex(size_t index) {
if (index > AllocationSpace::LAST_SPACE) {
return std::nullopt;
}
static_assert(AllocationSpace::FIRST_SPACE == 0);
return {static_cast<AllocationSpace>(index)};
}
static inline bool IsYoungGenerationCollector(GarbageCollector collector) {
return collector == GarbageCollector::SCAVENGER ||
collector == GarbageCollector::MINOR_MARK_SWEEPER;
}
V8_EXPORT_PRIVATE bool IsFreeSpaceValid(const FreeSpace* object) const;
static inline GarbageCollector YoungGenerationCollector() {
return (v8_flags.minor_ms) ? GarbageCollector::MINOR_MARK_SWEEPER
: GarbageCollector::SCAVENGER;
}
static inline void CopyBlock(Address dst, Address src, size_t byte_size);
perfetto::NamedTrack tracing_track() const { return tracing_track_; }
bool is_gc_tracing_category_enabled() const {
return *gc_tracing_category_enabled_;
}
enum class StackScanMode { kNone, kFull, kSelective };
StackScanMode ConservativeStackScanningModeForMinorGC() const {
if (v8_flags.scavenger_conservative_object_pinning) {
return StackScanMode::kFull;
}
if (selective_stack_scan_start_address_.has_value()) {
DCHECK(IsGCWithStack());
return StackScanMode::kSelective;
}
return StackScanMode::kNone;
}
StackScanMode ConservativeStackScanningModeForMajorGC() const {
if (v8_flags.conservative_stack_scanning) {
return StackScanMode::kFull;
}
if (selective_stack_scan_start_address_.has_value()) {
DCHECK(IsGCWithStack());
return StackScanMode::kSelective;
}
return StackScanMode::kNone;
}
bool ShouldUsePrecisePinningForMinorGC() const {
return v8_flags.scavenger_precise_object_pinning;
}
bool ShouldUsePrecisePinningForMajorGC() const {
return v8_flags.precise_object_pinning;
}
EphemeronRememberedSet* ephemeron_remembered_set() {
return ephemeron_remembered_set_.get();
}
HeapProfiler* heap_profiler() const { return heap_profiler_.get(); }
void NotifyDeserializationComplete();
void WeakenDescriptorArrays(
GlobalHandleVector<DescriptorArray> strong_descriptor_arrays);
void NotifyBootstrapComplete();
enum class OldGenerationExpansionNotificationOrigin {
kFromClientHeap,
kFromSameHeap,
};
void NotifyOldGenerationExpansion(
LocalHeap* local_heap, AllocationSpace space, MutablePageMetadata* chunk,
OldGenerationExpansionNotificationOrigin =
OldGenerationExpansionNotificationOrigin::kFromSameHeap);
size_t NewSpaceSize();
size_t NewSpaceCapacity() const;
size_t NewSpaceTargetCapacity() const;
template <typename TSlot>
V8_EXPORT_PRIVATE void MoveRange(Tagged<HeapObject> dst_object,
TSlot dst_slot, TSlot src_slot, int len,
WriteBarrierMode mode);
template <typename TSlot>
V8_EXPORT_PRIVATE void CopyRange(Tagged<HeapObject> dst_object,
TSlot dst_slot, TSlot src_slot, int len,
WriteBarrierMode mode);
V8_EXPORT_PRIVATE void CreateFillerObjectAt(
Address addr, int size,
ClearFreedMemoryMode clear_memory_mode =
ClearFreedMemoryMode::kDontClearFreedMemory,
std::optional<AllocationType> allocation_type = {});
void CreateFillerObjectAtBackground(const WritableFreeSpace& free_space);
bool CanMoveObjectStart(Tagged<HeapObject> object);
bool IsImmovable(Tagged<HeapObject> object);
V8_EXPORT_PRIVATE Tagged<FixedArrayBase> LeftTrimFixedArray(
Tagged<FixedArrayBase> obj, int elements_to_trim);
#define RIGHT_TRIMMABLE_ARRAY_LIST(V) \
V(ArrayList) \
V(ByteArray) \
V(FixedArray) \
V(FixedDoubleArray) \
V(TransitionArray) \
V(WeakFixedArray)
template <typename Array>
void RightTrimArray(Tagged<Array> object, int new_capacity, int old_capacity);
inline Tagged<Boolean> ToBoolean(bool condition);
V8_EXPORT_PRIVATE int NotifyContextDisposed(bool has_dependent_context);
void set_native_contexts_list(Tagged<Object> object) {
native_contexts_list_.store(object.ptr(), std::memory_order_release);
}
Tagged<Object> native_contexts_list() const {
return Tagged<Object>(
native_contexts_list_.load(std::memory_order_acquire));
}
V8_EXPORT_PRIVATE void AddToWeakNativeContextList(Tagged<Context> context);
void set_allocation_sites_list(
Tagged<UnionOf<Smi, Undefined, AllocationSiteWithWeakNext>> object) {
allocation_sites_list_ = object;
}
Tagged<UnionOf<Smi, Undefined, AllocationSiteWithWeakNext>>
allocation_sites_list() {
return allocation_sites_list_;
}
void set_dirty_js_finalization_registries_list(Tagged<Object> object) {
dirty_js_finalization_registries_list_ = object;
}
Tagged<Object> dirty_js_finalization_registries_list() {
return dirty_js_finalization_registries_list_;
}
void set_dirty_js_finalization_registries_list_tail(Tagged<Object> object) {
dirty_js_finalization_registries_list_tail_ = object;
}
Tagged<Object> dirty_js_finalization_registries_list_tail() {
return dirty_js_finalization_registries_list_tail_;
}
Address allocation_sites_list_address() {
return reinterpret_cast<Address>(&allocation_sites_list_);
}
void ForeachAllocationSite(
Tagged<Object> list,
const std::function<void(Tagged<AllocationSite>)>& visitor);
int ms_count() const { return ms_count_; }
bool AllowedToBeMigrated(Tagged<Map> map, Tagged<HeapObject> object,
AllocationSpace dest);
void CheckHandleCount();
void PrintShortHeapStatistics();
void PrintFreeListsStats();
void DumpJSONHeapStatistics(std::stringstream& stream);
inline HeapState gc_state() const {
return gc_state_.load(std::memory_order_relaxed);
}
V8_EXPORT_PRIVATE void SetGCState(HeapState state);
bool IsTearingDown() const { return gc_state() == TEAR_DOWN; }
bool IsInGC() const {
HeapState state = gc_state();
return state != NOT_IN_GC && state != TEAR_DOWN;
}
bool force_oom() const { return force_oom_; }
bool ignore_local_gc_requests() const {
return ignore_local_gc_requests_depth_ > 0;
}
bool IsAllocationObserverActive() const {
return pause_allocation_observers_depth_ == 0;
}
bool IsGCWithMainThreadStack() const;
V8_EXPORT_PRIVATE bool IsGCWithStack() const;
bool CanShortcutStringsDuringGC(GarbageCollector collector) const;
void PerformRequestedGC(LocalHeap* local_heap);
void CreateReadOnlyApiObjects();
void CreateMutableApiObjects();
V8_EXPORT_PRIVATE void MemoryPressureNotification(
v8::MemoryPressureLevel level, bool is_isolate_locked);
void CheckMemoryPressure();
V8_EXPORT_PRIVATE void AddNearHeapLimitCallback(v8::NearHeapLimitCallback,
void* data);
V8_EXPORT_PRIVATE void RemoveNearHeapLimitCallback(
v8::NearHeapLimitCallback callback, size_t heap_limit);
V8_EXPORT_PRIVATE void AutomaticallyRestoreInitialHeapLimit(
double threshold_percent);
V8_EXPORT_PRIVATE void AppendArrayBufferExtension(
ArrayBufferExtension* extension);
V8_EXPORT_PRIVATE void ResizeArrayBufferExtension(
ArrayBufferExtension* extension, int64_t delta);
void DetachArrayBufferExtension(ArrayBufferExtension* extension);
V8_EXPORT_PRIVATE void ExpandNewSpaceSizeForTesting();
V8_EXPORT_PRIVATE void ReduceNewSpaceSizeForTesting();
IsolateSafepoint* safepoint() { return safepoint_.get(); }
V8_EXPORT_PRIVATE double MonotonicallyIncreasingTimeInMs() const;
#if DEBUG
void VerifyNewSpaceTop();
#endif
void RecordStats(HeapStats* stats);
V8_EXPORT_PRIVATE void ReportStatsAsCrashKeys(const HeapStats& heap_stats);
bool MeasureMemory(std::unique_ptr<v8::MeasureMemoryDelegate> delegate,
v8::MeasureMemoryExecution execution);
std::unique_ptr<v8::MeasureMemoryDelegate> CreateDefaultMeasureMemoryDelegate(
v8::Local<v8::Context> context, v8::Local<v8::Promise::Resolver> promise,
v8::MeasureMemoryMode mode);
void IncrementDeferredCounts(
base::Vector<const v8::Isolate::UseCounterFeature> features);
int NextScriptId();
int NextDebuggingId();
int NextStackTraceId();
inline uint32_t GetNextTemplateSerialNumber();
void SetSerializedObjects(Tagged<HeapObject> objects);
void SetSerializedGlobalProxySizes(Tagged<FixedArray> sizes);
void SetBasicBlockProfilingData(DirectHandle<ArrayList> list);
void RememberUnmappedPage(Address page, bool compacted);
uint64_t external_memory_hard_limit() {
return external_memory_.low_since_mark_compact() +
max_old_generation_size() / 2;
}
V8_INLINE uint64_t external_memory() const;
V8_EXPORT_PRIVATE uint64_t external_memory_limit_for_interrupt();
V8_EXPORT_PRIVATE uint64_t external_memory_soft_limit();
uint64_t UpdateExternalMemory(int64_t delta);
uint64_t backing_store_bytes() const;
void CompactWeakArrayLists();
V8_EXPORT_PRIVATE void AddRetainedMaps(DirectHandle<NativeContext> context,
GlobalHandleVector<Map> maps);
void OnMoveEvent(Tagged<HeapObject> source, Tagged<HeapObject> target,
int size_in_bytes);
bool deserialization_complete() const { return deserialization_complete_; }
V8_INLINE bool CanSafepoint() const { return deserialization_complete(); }
bool HasLowAllocationRate();
bool HasHighFragmentation();
void ActivateMemoryReducerIfNeeded();
V8_EXPORT_PRIVATE bool ShouldOptimizeForMemoryUsage();
V8_EXPORT_PRIVATE bool ShouldOptimizeForBattery() const;
bool HighMemoryPressure() {
return memory_pressure_level_.load(std::memory_order_relaxed) !=
v8::MemoryPressureLevel::kNone;
}
bool CollectionRequested();
void RestoreHeapLimit(size_t heap_limit) {
size_t min_limit = SizeOfObjects() + SizeOfObjects() / 4;
SetOldGenerationAndGlobalMaximumSize(
std::min(max_old_generation_size(), std::max(heap_limit, min_limit)),
physical_memory());
}
void ConfigureHeap(const v8::ResourceConstraints& constraints,
v8::CppHeap* cpp_heap);
void ConfigureHeapDefault();
void SetUp(LocalHeap* main_thread_local_heap);
void SetUpFromReadOnlyHeap(ReadOnlyHeap* ro_heap);
void ReplaceReadOnlySpace(SharedReadOnlySpace* shared_ro_space);
void SetUpSpaces();
void InitializeMainThreadLocalHeap(LocalHeap* main_thread_local_heap);
void InitializeHashSeed();
static void InitializeOncePerProcess();
bool CreateReadOnlyHeapObjects();
bool CreateMutableHeapObjects();
void CreateObjectStats();
void StartTearDown();
void TearDownWithSharedHeap();
void TearDown();
bool HasBeenSetUp() const;
V8_INLINE Address NewSpaceTop();
V8_INLINE Address NewSpaceLimit();
NewSpace* new_space() const { return new_space_; }
inline PagedNewSpace* paged_new_space() const;
inline SemiSpaceNewSpace* semi_space_new_space() const;
OldSpace* old_space() const { return old_space_; }
inline StickySpace* sticky_space() const;
CodeSpace* code_space() const { return code_space_; }
SharedSpace* shared_space() const { return shared_space_; }
OldLargeObjectSpace* lo_space() const { return lo_space_; }
CodeLargeObjectSpace* code_lo_space() const { return code_lo_space_; }
SharedLargeObjectSpace* shared_lo_space() const { return shared_lo_space_; }
NewLargeObjectSpace* new_lo_space() const { return new_lo_space_; }
ReadOnlySpace* read_only_space() const { return read_only_space_; }
TrustedSpace* trusted_space() const { return trusted_space_; }
SharedTrustedSpace* shared_trusted_space() const {
return shared_trusted_space_;
}
TrustedLargeObjectSpace* trusted_lo_space() const {
return trusted_lo_space_;
}
SharedTrustedLargeObjectSpace* shared_trusted_lo_space() const {
return shared_trusted_lo_space_;
}
PagedSpace* shared_allocation_space() const {
return shared_allocation_space_;
}
OldLargeObjectSpace* shared_lo_allocation_space() const {
return shared_lo_allocation_space_;
}
SharedTrustedSpace* shared_trusted_allocation_space() const {
return shared_trusted_allocation_space_;
}
SharedTrustedLargeObjectSpace* shared_trusted_lo_allocation_space() const {
return shared_trusted_lo_allocation_space_;
}
inline PagedSpace* paged_space(int index) const;
inline Space* space(int index) const;
inline Space* space(AllocationSpace allocation_space) const;
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable::Space* young_external_pointer_space() {
return &young_external_pointer_space_;
}
ExternalPointerTable::Space* old_external_pointer_space() {
return &old_external_pointer_space_;
}
ExternalPointerTable::Space* read_only_external_pointer_space() {
return &read_only_external_pointer_space_;
}
CppHeapPointerTable::Space* cpp_heap_pointer_space() {
return &cpp_heap_pointer_space_;
}
#endif
#ifdef V8_ENABLE_SANDBOX
TrustedPointerTable::Space* trusted_pointer_space() {
return &trusted_pointer_space_;
}
CodePointerTable::Space* code_pointer_space() { return &code_pointer_space_; }
#endif
JSDispatchTable::Space* js_dispatch_table_space() {
return &js_dispatch_table_space_;
}
GCTracer* tracer() { return tracer_.get(); }
const GCTracer* tracer() const { return tracer_.get(); }
MemoryAllocator* memory_allocator() { return memory_allocator_.get(); }
const MemoryAllocator* memory_allocator() const {
return memory_allocator_.get();
}
inline Isolate* isolate() const;
inline bool IsMainThread() const;
MarkCompactCollector* mark_compact_collector() {
return mark_compact_collector_.get();
}
MinorMarkSweepCollector* minor_mark_sweep_collector() {
return minor_mark_sweep_collector_.get();
}
Sweeper* sweeper() { return sweeper_.get(); }
ArrayBufferSweeper* array_buffer_sweeper() const {
return array_buffer_sweeper_.get();
}
const base::AddressRegion& code_region();
CodeRange* code_range() {
#ifdef V8_COMPRESS_POINTERS
return code_range_;
#else
return code_range_.get();
#endif
}
inline Address code_range_base();
LocalHeap* main_thread_local_heap() { return main_thread_local_heap_; }
Heap* AsHeap() { return this; }
V8_INLINE RootsTable& roots_table();
#define ROOT_ACCESSOR(type, name, CamelName) inline Tagged<type> name();
MUTABLE_ROOT_LIST(ROOT_ACCESSOR)
#undef ROOT_ACCESSOR
V8_INLINE void SetRootMaterializedObjects(Tagged<FixedArray> objects);
V8_INLINE void SetRootScriptList(Tagged<Object> value);
V8_INLINE void SetRootNoScriptSharedFunctionInfos(Tagged<Object> value);
V8_INLINE void SetMessageListeners(Tagged<ArrayList> value);
V8_INLINE void SetFunctionsMarkedForManualOptimization(
Tagged<Object> bytecode);
V8_INLINE void SetSmiStringCache(Tagged<SmiStringCache> cache);
V8_INLINE void SetDoubleStringCache(Tagged<DoubleStringCache> cache);
#if V8_ENABLE_WEBASSEMBLY
V8_INLINE void SetWasmCanonicalRtts(Tagged<WeakFixedArray> rtts);
V8_INLINE void SetJSToWasmWrappers(
Tagged<WeakFixedArray> js_to_wasm_wrappers);
#endif
StrongRootsEntry* RegisterStrongRoots(const char* label, FullObjectSlot start,
FullObjectSlot end);
void UnregisterStrongRoots(StrongRootsEntry* entry);
void UpdateStrongRoots(StrongRootsEntry* entry, FullObjectSlot start,
FullObjectSlot end);
void SetBuiltinsConstantsTable(Tagged<FixedArray> cache);
void SetDetachedContexts(Tagged<WeakArrayList> detached_contexts);
void EnqueueDirtyJSFinalizationRegistry(
Tagged<JSFinalizationRegistry> finalization_registry,
std::function<void(Tagged<HeapObject> object, ObjectSlot slot,
Tagged<HeapObject> target)>
gc_notify_updated_slot,
WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER);
MaybeDirectHandle<JSFinalizationRegistry>
DequeueDirtyJSFinalizationRegistry();
void RemoveDirtyFinalizationRegistriesOnContext(
Tagged<NativeContext> context);
bool HasDirtyJSFinalizationRegistries();
void PostFinalizationRegistryCleanupTaskIfNeeded();
void set_is_finalization_registry_cleanup_task_posted(bool posted) {
is_finalization_registry_cleanup_task_posted_ = posted;
}
bool is_finalization_registry_cleanup_task_posted() {
return is_finalization_registry_cleanup_task_posted_;
}
V8_EXPORT_PRIVATE void KeepDuringJob(DirectHandle<HeapObject> target);
void ClearKeptObjects();
V8_EXPORT_PRIVATE void EnableInlineAllocation();
V8_EXPORT_PRIVATE void DisableInlineAllocation();
V8_EXPORT_PRIVATE void CollectGarbage(
AllocationSpace space, GarbageCollectionReason gc_reason,
const GCCallbackFlags gc_callback_flags = kNoGCCallbackFlags,
PerformHeapLimitCheck check_heap_limit_reached =
PerformHeapLimitCheck::kYes,
PerformIneffectiveMarkCompactCheck check_ineffective_mark_compact =
PerformIneffectiveMarkCompactCheck::kYes);
V8_EXPORT_PRIVATE void CollectAllGarbage(
GCFlags gc_flags, GarbageCollectionReason gc_reason,
const GCCallbackFlags gc_callback_flags = kNoGCCallbackFlags,
PerformHeapLimitCheck check_heap_limit_reached =
PerformHeapLimitCheck::kYes);
V8_EXPORT_PRIVATE void CollectAllAvailableGarbage(
GarbageCollectionReason gc_reason);
V8_EXPORT_PRIVATE void PreciseCollectAllGarbage(
GCFlags gc_flags, GarbageCollectionReason gc_reason,
const GCCallbackFlags gc_callback_flags = kNoGCCallbackFlags);
V8_EXPORT_PRIVATE bool CollectGarbageShared(
LocalHeap* local_heap, GarbageCollectionReason gc_reason);
V8_EXPORT_PRIVATE bool TriggerAndWaitForGCFromBackgroundThread(
LocalHeap* local_heap, RequestedGCKind kind);
V8_EXPORT_PRIVATE void CollectGarbageWithRetry(
AllocationSpace space, GCFlags gc_flags,
GarbageCollectionReason gc_reason,
const GCCallbackFlags gc_callback_flags);
void HandleExternalMemoryInterrupt();
using GetExternallyAllocatedMemoryInBytesCallback =
v8::Isolate::GetExternallyAllocatedMemoryInBytesCallback;
void SetGetExternallyAllocatedMemoryInBytesCallback(
GetExternallyAllocatedMemoryInBytesCallback callback) {
external_memory_callback_ = callback;
}
void HandleGCRequest();
enum class IterateRootsMode { kMainIsolate, kClientIsolate };
void IterateRoots(
RootVisitor* v, base::EnumSet<SkipRoot> options,
IterateRootsMode roots_mode = IterateRootsMode::kMainIsolate);
void IterateRootsIncludingClients(RootVisitor* v,
base::EnumSet<SkipRoot> options);
void IterateSmiRoots(RootVisitor* v);
void IterateWeakRoots(RootVisitor* v, base::EnumSet<SkipRoot> options);
void IterateWeakGlobalHandles(RootVisitor* v);
void IterateBuiltins(RootVisitor* v);
void IterateStackRoots(RootVisitor* v);
void IterateConservativeStackRoots(
RootVisitor* root_visitor,
IterateRootsMode roots_mode = IterateRootsMode::kMainIsolate);
void IterateConservativeStackRoots(::heap::base::StackVisitor* stack_visitor,
StackScanMode stack_scan_mode);
void IterateRootsForPrecisePinning(RootVisitor* visitor);
uint8_t* IsMarkingFlagAddress();
uint8_t* IsMinorMarkingFlagAddress();
void ClearRecordedSlotRange(Address start, Address end);
static int InsertIntoRememberedSetFromCode(MutablePageMetadata* chunk,
size_t slot_offset);
static void VerifySkippedWriteBarrier(Address object, Address value);
static void VerifySkippedIndirectWriteBarrier(Address object);
#ifdef DEBUG
void VerifySlotRangeHasNoRecordedSlots(Address start, Address end);
#endif
GCFlags GCFlagsForIncrementalMarking() {
return ShouldOptimizeForMemoryUsage() ? GCFlag::kReduceMemoryFootprint
: GCFlag::kNoFlags;
}
V8_EXPORT_PRIVATE void StartIncrementalMarking(
GCFlags gc_flags, GarbageCollectionReason gc_reason,
GCCallbackFlags gc_callback_flags = GCCallbackFlags::kNoGCCallbackFlags,
GarbageCollector collector = GarbageCollector::MARK_COMPACTOR,
const char* reason = "missing reason");
V8_EXPORT_PRIVATE void StartIncrementalMarkingOnInterrupt();
V8_EXPORT_PRIVATE void StartIncrementalMarkingIfAllocationLimitIsReached(
LocalHeap* local_heap, GCFlags gc_flags,
GCCallbackFlags gc_callback_flags = GCCallbackFlags::kNoGCCallbackFlags);
V8_EXPORT_PRIVATE void FinalizeIncrementalMarkingAtomically(
GarbageCollectionReason gc_reason);
V8_EXPORT_PRIVATE void FinalizeIncrementalMarkingAtomicallyIfRunning(
GarbageCollectionReason gc_reason);
V8_EXPORT_PRIVATE void CompleteSweepingFull(CompleteSweepingReason reason);
void CompleteSweepingYoung(CompleteSweepingReason reason);
void EnsureSweepingCompletedForObject(Tagged<HeapObject> object);
IncrementalMarking* incremental_marking() const {
return incremental_marking_.get();
}
ConcurrentMarking* concurrent_marking() const {
return concurrent_marking_.get();
}
void NotifyObjectLayoutChange(
Tagged<HeapObject> object, const DisallowGarbageCollection&,
InvalidateRecordedSlots invalidate_recorded_slots,
InvalidateExternalPointerSlots invalidate_external_pointer_slots,
int new_size = 0);
V8_EXPORT_PRIVATE static void NotifyObjectLayoutChangeDone(
Tagged<HeapObject> object);
void NotifyObjectSizeChange(Tagged<HeapObject>, int old_size, int new_size,
ClearRecordedSlots clear_recorded_slots);
void SetConstructStubCreateDeoptPCOffset(int pc_offset);
void SetConstructStubInvokeDeoptPCOffset(int pc_offset);
void SetDeoptPCOffsetAfterAdaptShadowStack(int pc_offset);
void SetInterpreterEntryReturnPCOffset(int pc_offset);
void DeoptMarkedAllocationSites();
v8::CppHeap* cpp_heap() const { return cpp_heap_; }
std::optional<StackState> overridden_stack_state() const;
V8_EXPORT_PRIVATE void SetStackStart();
V8_EXPORT_PRIVATE ::heap::base::Stack& stack();
V8_EXPORT_PRIVATE const ::heap::base::Stack& stack() const;
V8_EXPORT_PRIVATE
void SetEmbedderRootsHandler(EmbedderRootsHandler* handler);
EmbedderRootsHandler* GetEmbedderRootsHandler() const;
inline void RegisterExternalString(Tagged<String> string);
V8_EXPORT_PRIVATE void UpdateExternalString(Tagged<String> string,
size_t old_payload,
size_t new_payload);
inline void FinalizeExternalString(Tagged<String> string);
static inline bool InFromPage(Tagged<Object> object);
static inline bool InFromPage(Tagged<MaybeObject> object);
static inline bool InFromPage(Tagged<HeapObject> heap_object);
static inline bool InToPage(Tagged<Object> object);
static inline bool InToPage(Tagged<MaybeObject> object);
static inline bool InToPage(Tagged<HeapObject> heap_object);
inline bool InOldSpace(Tagged<Object> object);
V8_EXPORT_PRIVATE bool Contains(Tagged<HeapObject> value) const;
V8_EXPORT_PRIVATE bool ContainsCode(Tagged<HeapObject> value) const;
V8_EXPORT_PRIVATE bool SharedHeapContains(Tagged<HeapObject> value) const;
V8_EXPORT_PRIVATE bool MustBeInSharedOldSpace(Tagged<HeapObject> value);
V8_EXPORT_PRIVATE bool InSpace(Tagged<HeapObject> value,
AllocationSpace space) const;
V8_EXPORT_PRIVATE bool InSpaceSlow(Address addr, AllocationSpace space) const;
static inline Heap* FromWritableHeapObject(Tagged<HeapObject> obj);
V8_EXPORT_PRIVATE bool CanReferenceHeapObject(Tagged<HeapObject> obj);
size_t NumberOfTrackedHeapObjectTypes();
size_t ObjectCountAtLastGC(size_t index);
size_t ObjectSizeAtLastGC(size_t index);
bool GetObjectTypeName(size_t index, const char** object_type,
const char** object_sub_type);
size_t NumberOfNativeContexts();
size_t NumberOfDetachedContexts();
void CollectCodeStatistics();
V8_EXPORT_PRIVATE size_t MaxReserved() const;
size_t MaxSemiSpaceSize() { return max_semi_space_size_; }
size_t InitialSemiSpaceSize() { return initial_semispace_size_; }
size_t MaxOldGenerationSize() { return max_old_generation_size(); }
V8_EXPORT_PRIVATE static size_t AllocatorLimitOnMaxOldGenerationSize(
uint64_t physical_memory);
V8_EXPORT_PRIVATE static size_t OldGenerationSizeFromPhysicalMemory(
uint64_t physical_memory);
V8_EXPORT_PRIVATE static void GenerationSizesFromHeapSize(
uint64_t physical_memory, size_t heap_size, size_t* young_generation_size,
size_t* old_generation_size);
V8_EXPORT_PRIVATE static size_t YoungGenerationSizeFromPhysicalMemory(
uint64_t physical_memory);
V8_EXPORT_PRIVATE static size_t YoungGenerationSizeFromHeapSize(
uint64_t physical_memory, size_t heap_size);
V8_EXPORT_PRIVATE static size_t YoungGenerationSizeFromSemiSpaceSize(
size_t semi_space_size);
V8_EXPORT_PRIVATE static size_t SemiSpaceSizeFromYoungGenerationSize(
size_t young_generation_size);
V8_EXPORT_PRIVATE static size_t MinYoungGenerationSize();
V8_EXPORT_PRIVATE static size_t MinOldGenerationSize();
V8_EXPORT_PRIVATE static size_t MaxOldGenerationSizeFromPhysicalMemory(
uint64_t physical_memory);
uint64_t physical_memory() const {
DCHECK(configured_);
return physical_memory_;
}
size_t Capacity();
V8_EXPORT_PRIVATE size_t OldGenerationCapacity() const;
base::Mutex* heap_expansion_mutex() { return &heap_expansion_mutex_; }
size_t CommittedMemory();
size_t CommittedOldGenerationMemory();
size_t CommittedMemoryExecutable();
size_t CommittedPhysicalMemory();
size_t MaximumCommittedMemory() { return maximum_committed_; }
void UpdateMaximumCommitted();
size_t Available();
V8_EXPORT_PRIVATE size_t SizeOfObjects();
V8_EXPORT_PRIVATE size_t TotalGlobalHandlesSize();
V8_EXPORT_PRIVATE size_t UsedGlobalHandlesSize();
void UpdateSurvivalStatistics(int start_new_space_size);
inline void IncrementPromotedObjectsSize(size_t object_size) {
promoted_objects_size_ += object_size;
}
inline size_t promoted_objects_size() { return promoted_objects_size_; }
inline void IncrementNewSpaceSurvivingObjectSize(size_t object_size) {
new_space_surviving_object_size_ += object_size;
}
inline size_t new_space_surviving_object_size() {
return new_space_surviving_object_size_;
}
inline size_t SurvivedYoungObjectSize() {
return promoted_objects_size_ + new_space_surviving_object_size_;
}
inline void IncrementNodesDiedInNewSpace(int count) {
nodes_died_in_new_space_ += count;
}
inline void IncrementNodesCopiedInNewSpace() { nodes_copied_in_new_space_++; }
inline void IncrementNodesPromoted() { nodes_promoted_++; }
inline void IncrementYoungSurvivorsCounter(size_t survived) {
survived_since_last_expansion_ += survived;
}
V8_EXPORT_PRIVATE size_t NewSpaceAllocationCounter() const;
void SetNewSpaceAllocationCounterForTesting(size_t new_value) {
new_space_allocation_counter_ = new_value;
}
void UpdateOldGenerationAllocationCounter() {
old_generation_allocation_counter_at_last_gc_ =
OldGenerationAllocationCounter();
}
size_t OldGenerationAllocationCounter() {
return old_generation_allocation_counter_at_last_gc_ +
PromotedSinceLastGC();
}
size_t EmbedderAllocationCounter() const;
void set_old_generation_allocation_counter_at_last_gc(size_t new_value) {
old_generation_allocation_counter_at_last_gc_ = new_value;
}
GCEpoch gc_count() const { return gc_count_; }
bool is_current_gc_forced() const { return is_current_gc_forced_; }
GarbageCollector current_or_last_garbage_collector() const {
return current_or_last_garbage_collector_;
}
bool ShouldCurrentGCKeepAgesUnchanged() const {
return is_current_gc_forced_ || is_current_gc_for_heap_profiler_;
}
V8_EXPORT_PRIVATE size_t OldGenerationSizeOfObjects() const;
V8_EXPORT_PRIVATE size_t OldGenerationWastedBytes() const;
V8_EXPORT_PRIVATE size_t OldGenerationConsumedBytes() const;
V8_EXPORT_PRIVATE size_t YoungGenerationSizeOfObjects() const;
V8_EXPORT_PRIVATE size_t YoungGenerationWastedBytes() const;
V8_EXPORT_PRIVATE size_t YoungGenerationConsumedBytes() const;
V8_EXPORT_PRIVATE size_t EmbedderSizeOfObjects() const;
V8_EXPORT_PRIVATE size_t GlobalSizeOfObjects() const;
V8_EXPORT_PRIVATE size_t GlobalWastedBytes() const;
V8_EXPORT_PRIVATE size_t GlobalConsumedBytes() const;
V8_EXPORT_PRIVATE size_t OldGenerationConsumedBytesAtLastGC() const;
V8_EXPORT_PRIVATE size_t GlobalConsumedBytesAtLastGC() const;
V8_EXPORT_PRIVATE size_t OldGenerationAllocationLimitForTesting() const;
V8_EXPORT_PRIVATE size_t GlobalAllocationLimitForTesting() const;
V8_EXPORT_PRIVATE size_t GetExternalStrinBytesForTesting() const;
bool AllocationLimitOvershotByLargeMargin() const;
inline V8_EXPORT_PRIVATE int MaxRegularHeapObjectSize(
AllocationType allocation);
void AddGCPrologueCallback(v8::Isolate::GCCallbackWithData callback,
GCType gc_type_filter, void* data);
void RemoveGCPrologueCallback(v8::Isolate::GCCallbackWithData callback,
void* data);
void AddGCEpilogueCallback(v8::Isolate::GCCallbackWithData callback,
GCType gc_type_filter, void* data);
void RemoveGCEpilogueCallback(v8::Isolate::GCCallbackWithData callback,
void* data);
void CallGCPrologueCallbacks(GCType gc_type, GCCallbackFlags flags,
GCTracer::Scope::ScopeId scope_id);
void CallGCEpilogueCallbacks(GCType gc_type, GCCallbackFlags flags,
GCTracer::Scope::ScopeId scope_id);
V8_EXPORT_PRIVATE Tagged<HeapObject> PrecedeWithFiller(
Tagged<HeapObject> object, int filler_size);
V8_EXPORT_PRIVATE Tagged<HeapObject> PrecedeWithFillerBackground(
Tagged<HeapObject> object, int filler_size);
V8_WARN_UNUSED_RESULT Tagged<HeapObject> AlignWithFillerBackground(
Tagged<HeapObject> object, int object_size, int allocation_size,
AllocationAlignment alignment);
V8_EXPORT_PRIVATE void* AllocateExternalBackingStore(
const std::function<void*(size_t)>& allocate, size_t byte_length);
void AddAllocationObserversToAllSpaces(
AllocationObserver* observer, AllocationObserver* new_space_observer);
void RemoveAllocationObserversFromAllSpaces(
AllocationObserver* observer, AllocationObserver* new_space_observer);
inline bool IsPendingAllocation(Tagged<HeapObject> object);
inline bool IsPendingAllocation(Tagged<Object> object);
V8_EXPORT_PRIVATE void PublishMainThreadPendingAllocations();
V8_EXPORT_PRIVATE void AddHeapObjectAllocationTracker(
HeapObjectAllocationTracker* tracker);
V8_EXPORT_PRIVATE void RemoveHeapObjectAllocationTracker(
HeapObjectAllocationTracker* tracker);
bool has_heap_object_allocation_tracker() const {
return !allocation_trackers_.empty();
}
V8_EXPORT_PRIVATE Tagged<Code> FindCodeForInnerPointer(Address inner_pointer);
Tagged<GcSafeCode> GcSafeFindCodeForInnerPointer(Address inner_pointer);
std::optional<Tagged<GcSafeCode>> GcSafeTryFindCodeForInnerPointer(
Address inner_pointer);
std::optional<Tagged<InstructionStream>>
GcSafeTryFindInstructionStreamForInnerPointer(Address inner_pointer);
std::optional<Tagged<Code>> TryFindCodeForInnerPointerForPrinting(
Address inner_pointer);
bool GcSafeInstructionStreamContains(
Tagged<InstructionStream> instruction_stream, Address addr);
bool sweeping_in_progress() const { return sweeper_->sweeping_in_progress(); }
bool sweeping_in_progress_for_space(AllocationSpace space) const {
return sweeper_->sweeping_in_progress_for_space(space);
}
bool minor_sweeping_in_progress() const {
return sweeper_->minor_sweeping_in_progress();
}
bool major_sweeping_in_progress() const {
return sweeper_->major_sweeping_in_progress();
}
void FinishSweepingIfOutOfWork(CompleteSweepingReason reason);
enum class SweepingForcedFinalizationMode { kUnifiedHeap, kV8Only };
V8_EXPORT_PRIVATE void EnsureSweepingCompleted(
SweepingForcedFinalizationMode mode, CompleteSweepingReason reason);
void EnsureYoungSweepingCompleted();
void EnsureQuarantinedPagesSweepingCompleted();
#ifdef V8_ENABLE_ALLOCATION_TIMEOUT
void V8_EXPORT_PRIVATE set_allocation_timeout(int allocation_timeout);
#endif
#ifdef DEBUG
void VerifyCountersAfterSweeping();
void VerifyCountersBeforeConcurrentSweeping(GarbageCollector collector);
void VerifyCommittedPhysicalMemory();
void Print();
void PrintHandles();
void ReportCodeStatistics(const char* title);
#endif
void* GetRandomMmapAddr() {
void* result = v8::internal::GetRandomMmapAddr();
#if V8_TARGET_ARCH_X64
#if V8_OS_DARWIN
uintptr_t offset = reinterpret_cast<uintptr_t>(result) & kMmapRegionMask;
result = reinterpret_cast<void*>(mmap_region_base_ + offset);
#endif
#endif
return result;
}
V8_EXPORT_PRIVATE void MakeHeapIterable(CompleteSweepingReason reason);
V8_EXPORT_PRIVATE void Unmark();
V8_EXPORT_PRIVATE void DeactivateMajorGCInProgressFlag();
V8_EXPORT_PRIVATE void FreeLinearAllocationAreas();
V8_EXPORT_PRIVATE void FreeMainThreadLinearAllocationAreas();
V8_EXPORT_PRIVATE bool CanPromoteYoungAndExpandOldGeneration(
size_t size) const;
V8_EXPORT_PRIVATE bool CanExpandOldGeneration(size_t size) const;
V8_EXPORT_PRIVATE bool IsOldGenerationExpansionAllowed(
size_t size, const base::MutexGuard& expansion_mutex_witness) const;
bool ShouldReduceMemory() const {
return current_gc_flags_ & GCFlag::kReduceMemoryFootprint;
}
bool IsLastResortGC() { return current_gc_flags_ & GCFlag::kLastResort; }
MarkingState* marking_state() { return &marking_state_; }
NonAtomicMarkingState* non_atomic_marking_state() {
return &non_atomic_marking_state_;
}
PretenuringHandler* pretenuring_handler() { return &pretenuring_handler_; }
bool IsInlineAllocationEnabled() const { return inline_allocation_enabled_; }
V8_EXPORT_PRIVATE uint64_t AllocatedExternalMemorySinceMarkCompact() const;
size_t YoungExternalMemoryBytes() const;
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
TaskPriority priority = TaskPriority::kUserBlocking) const;
bool ShouldUseBackgroundThreads() const;
bool ShouldUseIncrementalMarking() const;
void AddTotalAllocatedBytes(size_t size) {
total_allocated_bytes_.fetch_add(size, std::memory_order_relaxed);
}
uint64_t GetTotalAllocatedBytes();
HeapAllocator* allocator() { return heap_allocator_; }
const HeapAllocator* allocator() const { return heap_allocator_; }
bool use_new_space() const {
DCHECK_IMPLIES(new_space(), !v8_flags.sticky_mark_bits);
return new_space() || v8_flags.sticky_mark_bits;
}
bool IsNewSpaceAllowedToGrowAboveTargetCapacity() const;
private:
class AllocationTrackerForDebugging;
void AttachCppHeap(v8::CppHeap* cpp_heap);
using ExternalStringTableUpdaterCallback =
Tagged<String> (*)(Heap* heap, FullObjectSlot pointer);
class ExternalStringTable {
public:
explicit ExternalStringTable(Heap* heap) : heap_(heap) {}
ExternalStringTable(const ExternalStringTable&) = delete;
ExternalStringTable& operator=(const ExternalStringTable&) = delete;
inline void AddString(Tagged<String> string);
bool Contains(Tagged<String> string);
void Iterate(RootVisitor* visitor);
void Update(base::FunctionRef<void(Tagged<HeapObject>)> callback);
void CleanUp();
void TearDown();
void UpdateReferences(
Heap::ExternalStringTableUpdaterCallback updater_func);
size_t GetBytes() const { return bytes_; }
private:
void Verify();
Heap* const heap_;
size_t bytes_{0};
std::vector<TaggedBase> old_strings_;
base::Mutex mutex_;
};
static const int kInitialEvalCacheSize = 64;
static const int kRememberedUnmappedPages = 128;
static const int kYoungSurvivalRateHighThreshold = 90;
static const int kYoungSurvivalRateAllowedDeviation = 15;
static const int kOldSurvivalRateLowThreshold = 10;
static const int kMaxMarkCompactsInIdleRound = 7;
Heap();
~Heap();
Heap(const Heap&) = delete;
Heap& operator=(const Heap&) = delete;
static bool IsRegularObjectAllocation(AllocationType allocation) {
return AllocationType::kYoung == allocation ||
AllocationType::kOld == allocation;
}
#define ROOT_ACCESSOR(type, name, CamelName) \
inline void set_##name(Tagged<type> value);
ROOT_LIST(ROOT_ACCESSOR)
#undef ROOT_ACCESSOR
int NumberOfScavengeTasks();
GarbageCollector SelectGarbageCollector(AllocationSpace space,
GarbageCollectionReason gc_reason,
const char** reason) const;
void CheckHeapLimitReached();
bool ReachedHeapLimit();
bool HasConsecutiveIneffectiveMarkCompact() const;
void MakeLinearAllocationAreasIterable();
void MarkSharedLinearAllocationAreasBlack();
void UnmarkSharedLinearAllocationAreas();
void FreeSharedLinearAllocationAreasAndResetFreeLists();
void PerformGarbageCollection(GarbageCollector collector,
GarbageCollectionReason gc_reason,
const char* collector_reason);
void PerformHeapVerification();
std::vector<Isolate*> PauseConcurrentThreadsInClients(
GarbageCollector collector);
void ResumeConcurrentThreadsInClients(std::vector<Isolate*> paused_clients);
void StaticRootsEnsureAllocatedSize(DirectHandle<HeapObject> obj,
int required);
bool CreateEarlyReadOnlyMapsAndObjects();
bool CreateImportantReadOnlyObjects();
bool CreateLateReadOnlyNonJSReceiverMaps();
bool CreateLateReadOnlyJSReceiverMaps();
bool CreateReadOnlyObjects();
void CreateInternalAccessorInfoObjects();
void CreateInitialMutableObjects();
enum class VerifyNoSlotsRecorded { kYes, kNo };
void CreateFillerObjectAtRaw(const WritableFreeSpace& free_space,
ClearFreedMemoryMode clear_memory_mode,
ClearRecordedSlots clear_slots_mode,
VerifyNoSlotsRecorded verify_no_slots_recorded);
void ResetAllAllocationSitesDependentCode(AllocationType allocation);
void EvaluateOldSpaceLocalPretenuring(uint64_t size_of_objects_before_gc);
void ReportStatisticsAfterGC();
void ActivateMemoryReducerIfNeededOnMainThread();
void ShrinkOldGenerationAllocationLimitIfNotConfigured();
void EnsureMinimumRemainingAllocationLimit(size_t at_least_remaining);
double ComputeMutatorUtilization(const char* tag, double mutator_speed,
std::optional<double> gc_speed);
bool HasLowYoungGenerationAllocationRate();
bool HasLowOldGenerationAllocationRate();
bool HasLowEmbedderAllocationRate();
enum class ResizeNewSpaceMode { kShrink, kGrow, kNone };
ResizeNewSpaceMode ShouldResizeNewSpace();
void StartResizeNewSpace();
void ResizeNewSpace();
void ExpandNewSpaceSize();
void ReduceNewSpaceSize();
void PrintMaxMarkingLimitReached();
void PrintMaxNewSpaceSizeReached();
int NextStressMarkingLimit();
void AddToRingBuffer(const char* string);
void GetFromRingBuffer(char* buffer);
static constexpr int kRetainMapEntrySize = 2;
void CompactRetainedMaps(Tagged<WeakArrayList> retained_maps);
void CollectGarbageOnMemoryPressure();
void EagerlyFreeExternalMemoryAndWasmCode();
bool InvokeNearHeapLimitCallback();
void InvokeIncrementalMarkingPrologueCallbacks();
void InvokeIncrementalMarkingEpilogueCallbacks();
Tagged<GcSafeCode> GcSafeGetCodeFromInstructionStream(
Tagged<HeapObject> instruction_stream, Address inner_pointer);
Tagged<Map> GcSafeMapOfHeapObject(Tagged<HeapObject> object);
void GarbageCollectionPrologue(GarbageCollectionReason gc_reason,
const v8::GCCallbackFlags gc_callback_flags);
void GarbageCollectionPrologueInSafepoint(GarbageCollector collector);
void GarbageCollectionEpilogue(GarbageCollector collector);
void GarbageCollectionEpilogueInSafepoint(GarbageCollector collector);
void MarkCompact();
void MinorMarkSweep();
void MarkCompactPrologue();
void MarkCompactEpilogue();
void Scavenge();
void ProcessAllWeakReferences(WeakObjectRetainer* retainer);
void ProcessNativeContexts(WeakObjectRetainer* retainer);
void ProcessAllocationSites(WeakObjectRetainer* retainer);
void ProcessDirtyJSFinalizationRegistries(WeakObjectRetainer* retainer);
void ProcessWeakListRoots(WeakObjectRetainer* retainer);
inline uint64_t OldGenerationAllocationLimitConsumedBytes() {
uint64_t bytes = OldGenerationConsumedBytes();
if (!v8_flags.external_memory_accounted_in_global_limit) {
bytes += AllocatedExternalMemorySinceMarkCompact();
}
return bytes;
}
inline size_t OldGenerationSpaceAvailable() {
uint64_t bytes = OldGenerationAllocationLimitConsumedBytes();
if (old_generation_allocation_limit() <= bytes) return 0;
return old_generation_allocation_limit() - static_cast<size_t>(bytes);
}
void UpdateTotalGCTime(base::TimeDelta duration);
bool IsIneffectiveMarkCompact(size_t old_generation_size, size_t global_size,
double mutator_utilization);
void CheckIneffectiveMarkCompact(size_t old_generation_size,
size_t global_size,
double mutator_utilization);
MemoryReducer* memory_reducer() { return memory_reducer_.get(); }
static const int kMaxLoadTimeMs = 7000;
V8_EXPORT_PRIVATE bool ShouldOptimizeForLoadTime() const;
V8_EXPORT_PRIVATE bool IsLoading() const;
void NotifyLoadingStarted();
void NotifyLoadingEnded();
size_t old_generation_allocation_limit() const {
return old_generation_allocation_limit_.load(std::memory_order_relaxed);
}
size_t global_allocation_limit() const {
return global_allocation_limit_.load(std::memory_order_relaxed);
}
bool using_initial_limit() const {
return using_initial_limit_.load(std::memory_order_relaxed);
}
void set_using_initial_limit(bool value) {
using_initial_limit_.store(value, std::memory_order_relaxed);
}
size_t max_old_generation_size() const {
return max_old_generation_size_.load(std::memory_order_relaxed);
}
size_t max_global_memory_size() const { return max_global_memory_size_; }
size_t min_old_generation_size() const { return min_old_generation_size_; }
void SetOldGenerationAndGlobalMaximumSize(size_t max_old_generation_size,
size_t physical_memory);
void SetOldGenerationAndGlobalAllocationLimit(
size_t new_old_generation_allocation_limit,
size_t new_global_allocation_limit,
const char* reason = __builtin_FUNCTION());
void ResetOldGenerationAndGlobalAllocationLimit();
bool always_allocate() const { return always_allocate_scope_count_ != 0; }
bool ShouldExpandOldGenerationOnSlowAllocation(LocalHeap* local_heap,
AllocationOrigin origin);
bool ShouldExpandYoungGenerationOnSlowAllocation(size_t allocation_size);
HeapGrowingMode CurrentHeapGrowingMode();
double PercentToOldGenerationLimit() const;
double PercentToGlobalMemoryLimit() const;
enum class IncrementalMarkingLimit {
kNoLimit,
kSoftLimit,
kHardLimit,
kFallbackForEmbedderLimit
};
std::pair<IncrementalMarkingLimit, const char*>
IncrementalMarkingLimitReached();
bool ShouldStressCompaction() const;
size_t GlobalMemoryAvailable();
void RecomputeLimits(GarbageCollector collector);
void RecomputeLimitsAfterLoadingIfNeeded();
struct LimitsComputationResult {
size_t old_generation_allocation_limit;
size_t global_allocation_limit;
};
LimitsComputationResult UpdateAllocationLimits(
LimitBounds boundaries, const char* caller = __builtin_FUNCTION());
V8_EXPORT_PRIVATE void StartMinorMSConcurrentMarkingIfNeeded();
bool MinorMSSizeTaskTriggerReached() const;
MinorGCJob* minor_gc_job() { return minor_gc_job_.get(); }
V8_WARN_UNUSED_RESULT AllocationResult
AllocateMap(AllocationType allocation_type, InstanceType instance_type,
int instance_size,
ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,
int inobject_properties = 0);
V8_WARN_UNUSED_RESULT V8_INLINE AllocationResult
AllocateRaw(int size_in_bytes, AllocationType allocation,
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kTaggedAligned);
enum AllocationRetryMode { kLightRetry, kRetryOrFail };
template <AllocationRetryMode mode>
V8_WARN_UNUSED_RESULT V8_INLINE Tagged<HeapObject> AllocateRawWith(
int size, AllocationType allocation,
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kTaggedAligned);
V8_WARN_UNUSED_RESULT inline Address AllocateRawOrFail(
int size, AllocationType allocation,
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kTaggedAligned);
V8_WARN_UNUSED_RESULT AllocationResult Allocate(DirectHandle<Map> map,
AllocationType allocation);
V8_WARN_UNUSED_RESULT AllocationResult
AllocatePartialMap(InstanceType instance_type, int instance_size);
void FinalizePartialMap(Tagged<Map> map);
void set_force_oom(bool value) { force_oom_ = value; }
void set_force_gc_on_next_allocation() {
force_gc_on_next_allocation_ = true;
}
inline bool IsPendingAllocationInternal(Tagged<HeapObject> object);
#ifdef DEBUG
V8_EXPORT_PRIVATE void IncrementObjectCounters();
#endif
std::vector<Handle<NativeContext>> FindAllNativeContexts();
std::vector<Tagged<WeakArrayList>> FindAllRetainedMaps();
MemoryMeasurement* memory_measurement() { return memory_measurement_.get(); }
AllocationType allocation_type_for_in_place_internalizable_strings() const {
return allocation_type_for_in_place_internalizable_strings_;
}
bool IsStressingScavenge();
void SetIsMarkingFlag(bool value);
void SetIsMinorMarkingFlag(bool value);
size_t PromotedSinceLastGC() {
size_t old_generation_size = OldGenerationSizeOfObjects();
return old_generation_size > old_generation_size_at_last_gc_
? old_generation_size - old_generation_size_at_last_gc_
: 0;
}
ExternalMemoryAccounting external_memory_;
Isolate* isolate_ = nullptr;
HeapAllocator* heap_allocator_ = nullptr;
size_t code_range_size_ = 0;
size_t max_semi_space_size_ = 0;
size_t min_semi_space_size_ = 0;
size_t initial_semispace_size_ = 0;
size_t min_old_generation_size_ = 0;
std::atomic<size_t> max_old_generation_size_{0};
size_t min_global_memory_size_ = 0;
size_t max_global_memory_size_ = 0;
size_t initial_max_old_generation_size_ = 0;
size_t initial_max_old_generation_size_threshold_ = 0;
size_t initial_old_generation_size_ = 0;
std::atomic<bool> using_initial_limit_ = true;
bool initial_size_overwritten_ = false;
bool preconfigured_old_generation_size_ = false;
size_t maximum_committed_ = 0;
size_t old_generation_capacity_after_bootstrap_ = 0;
size_t survived_since_last_expansion_ = 0;
std::atomic<size_t> always_allocate_scope_count_{0};
std::atomic<v8::MemoryPressureLevel> memory_pressure_level_;
std::vector<std::pair<v8::NearHeapLimitCallback, void*>>
near_heap_limit_callbacks_;
int contexts_disposed_ = 0;
NewSpace* new_space_ = nullptr;
OldSpace* old_space_ = nullptr;
CodeSpace* code_space_ = nullptr;
SharedSpace* shared_space_ = nullptr;
OldLargeObjectSpace* lo_space_ = nullptr;
CodeLargeObjectSpace* code_lo_space_ = nullptr;
NewLargeObjectSpace* new_lo_space_ = nullptr;
SharedLargeObjectSpace* shared_lo_space_ = nullptr;
ReadOnlySpace* read_only_space_ = nullptr;
TrustedSpace* trusted_space_ = nullptr;
SharedTrustedSpace* shared_trusted_space_ = nullptr;
TrustedLargeObjectSpace* trusted_lo_space_ = nullptr;
SharedTrustedLargeObjectSpace* shared_trusted_lo_space_ = nullptr;
PagedSpace* shared_allocation_space_ = nullptr;
OldLargeObjectSpace* shared_lo_allocation_space_ = nullptr;
SharedTrustedSpace* shared_trusted_allocation_space_ = nullptr;
SharedTrustedLargeObjectSpace* shared_trusted_lo_allocation_space_ = nullptr;
std::unique_ptr<Space> space_[LAST_SPACE + 1];
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable::Space young_external_pointer_space_;
ExternalPointerTable::Space old_external_pointer_space_;
ExternalPointerTable::Space read_only_external_pointer_space_;
CppHeapPointerTable::Space cpp_heap_pointer_space_;
#endif
#ifdef V8_ENABLE_SANDBOX
TrustedPointerTable::Space trusted_pointer_space_;
CodePointerTable::Space code_pointer_space_;
#endif
JSDispatchTable::Space js_dispatch_table_space_;
LocalHeap* main_thread_local_heap_ = nullptr;
std::atomic<HeapState> gc_state_{NOT_IN_GC};
int stress_marking_percentage_ = 0;
StressScavengeObserver* stress_scavenge_observer_ = nullptr;
std::atomic<double> max_marking_limit_reached_ = 0.0;
uint32_t ms_count_ = 0;
GCEpoch gc_count_ = kInitialGCEpoch;
std::atomic<int> consecutive_ineffective_mark_compacts_ = 0;
static const uintptr_t kMmapRegionMask = 0xFFFFFFFFu;
uintptr_t mmap_region_base_ = 0;
int remembered_unmapped_pages_index_ = 0;
Address remembered_unmapped_pages_[kRememberedUnmappedPages];
std::atomic<size_t> old_generation_allocation_limit_{0};
std::atomic<size_t> global_allocation_limit_{0};
std::atomic<Address> native_contexts_list_;
Tagged<UnionOf<Smi, Undefined, AllocationSiteWithWeakNext>>
allocation_sites_list_ = Smi::zero();
Tagged<Object> dirty_js_finalization_registries_list_ = Smi::zero();
Tagged<Object> dirty_js_finalization_registries_list_tail_ = Smi::zero();
GCCallbacks gc_prologue_callbacks_;
GCCallbacks gc_epilogue_callbacks_;
GetExternallyAllocatedMemoryInBytesCallback external_memory_callback_;
base::SmallVector<v8::Isolate::UseCounterFeature, 8> deferred_counters_;
size_t promoted_objects_size_ = 0;
double promotion_ratio_ = 0.0;
double promotion_rate_ = 0.0;
size_t new_space_surviving_object_size_ = 0;
size_t previous_new_space_surviving_object_size_ = 0;
double new_space_surviving_rate_ = 0.0;
int nodes_died_in_new_space_ = 0;
int nodes_copied_in_new_space_ = 0;
int nodes_promoted_ = 0;
base::TimeDelta total_gc_time_ms_;
double last_gc_time_ = 0.0;
std::unique_ptr<GCTracer> tracer_;
std::unique_ptr<Sweeper> sweeper_;
std::unique_ptr<MarkCompactCollector> mark_compact_collector_;
std::unique_ptr<MinorMarkSweepCollector> minor_mark_sweep_collector_;
std::unique_ptr<ScavengerCollector> scavenger_collector_;
std::unique_ptr<ArrayBufferSweeper> array_buffer_sweeper_;
std::unique_ptr<MemoryAllocator> memory_allocator_;
std::unique_ptr<IncrementalMarking> incremental_marking_;
std::unique_ptr<ConcurrentMarking> concurrent_marking_;
std::unique_ptr<MemoryMeasurement> memory_measurement_;
std::unique_ptr<MemoryReducer> memory_reducer_;
std::unique_ptr<ObjectStats> live_object_stats_;
std::unique_ptr<ObjectStats> dead_object_stats_;
std::unique_ptr<MinorGCJob> minor_gc_job_;
std::unique_ptr<AllocationObserver> stress_concurrent_allocation_observer_;
std::unique_ptr<AllocationTrackerForDebugging>
allocation_tracker_for_debugging_;
std::unique_ptr<EphemeronRememberedSet> ephemeron_remembered_set_;
std::unique_ptr<HeapProfiler> heap_profiler_;
std::shared_ptr<v8::TaskRunner> task_runner_;
#ifdef V8_COMPRESS_POINTERS
CodeRange* code_range_ = nullptr;
#else
std::unique_ptr<CodeRange> code_range_;
#endif
std::unique_ptr<CppHeap> owning_cpp_heap_;
v8::CppHeap* cpp_heap_ = nullptr;
EmbedderRootsHandler* embedder_roots_handler_ =
nullptr;
StackState embedder_stack_state_ = StackState::kMayContainHeapPointers;
std::optional<EmbedderStackStateOrigin> embedder_stack_state_origin_;
StrongRootsEntry* strong_roots_head_ = nullptr;
base::Mutex strong_roots_mutex_;
base::Mutex heap_expansion_mutex_;
bool need_to_remove_stress_concurrent_allocation_observer_ = false;
size_t new_space_allocation_counter_ = 0;
size_t old_generation_allocation_counter_at_last_gc_ = 0;
size_t old_generation_size_at_last_gc_{0};
size_t old_generation_wasted_at_last_gc_{0};
size_t embedder_size_at_last_gc_ = 0;
char trace_ring_buffer_[kTraceRingBufferSize];
bool ring_buffer_full_ = false;
size_t ring_buffer_end_ = 0;
bool configured_ = false;
GCFlags current_gc_flags_ = GCFlag::kNoFlags;
GCCallbackFlags current_gc_callback_flags_ =
GCCallbackFlags::kNoGCCallbackFlags;
std::unique_ptr<IsolateSafepoint> safepoint_;
bool is_current_gc_forced_ = false;
bool is_current_gc_for_heap_profiler_ = false;
GarbageCollector current_or_last_garbage_collector_ =
GarbageCollector::SCAVENGER;
ExternalStringTable external_string_table_;
const AllocationType allocation_type_for_in_place_internalizable_strings_;
std::unique_ptr<CollectionBarrier> collection_barrier_;
int ignore_local_gc_requests_depth_ = 0;
int gc_callbacks_depth_ = 0;
bool deserialization_complete_ = false;
int max_regular_code_object_size_ = 0;
bool inline_allocation_enabled_ = true;
int pause_allocation_observers_depth_ = 0;
bool force_oom_ = false;
bool force_gc_on_next_allocation_ = false;
bool delay_sweeper_tasks_for_testing_ = false;
std::vector<HeapObjectAllocationTracker*> allocation_trackers_;
bool is_finalization_registry_cleanup_task_posted_ = false;
MarkingState marking_state_;
NonAtomicMarkingState non_atomic_marking_state_;
PretenuringHandler pretenuring_handler_;
ResizeNewSpaceMode resize_new_space_mode_ = ResizeNewSpaceMode::kNone;
std::unique_ptr<MemoryBalancer> mb_;
static constexpr double kLoadTimeNotLoading = -1.0;
std::atomic<double> load_start_time_ms_{kLoadTimeNotLoading};
bool update_allocation_limits_after_loading_ = false;
std::optional<const void*> selective_stack_scan_start_address_;
uint64_t physical_memory_;
std::atomic<uint64_t> total_allocated_bytes_ = 0;
perfetto::NamedTrack tracing_track_;
perfetto::NamedTrack loading_track_;
const uint8_t* gc_tracing_category_enabled_ = nullptr;
size_t notify_context_disposed_counter_ = 1;
friend class ActivateMemoryReducerTask;
friend class AlwaysAllocateScope;
friend class ArrayBufferCollector;
friend class ArrayBufferSweeper;
friend class ConservativePinningScope;
friend class ConcurrentMarking;
friend class ConservativeTracedHandlesMarkingVisitor;
friend class CppHeap;
friend class EmbedderStackStateScope;
friend class EvacuateVisitorBase;
friend class GCCallbacksScope;
friend class GCTracer;
friend class HeapAllocator;
friend class HeapObjectIterator;
friend class HeapVerifier;
friend class IgnoreLocalGCRequests;
friend class IncrementalMarking;
friend class IncrementalMarkingJob;
friend class LargeObjectSpace;
friend class LocalHeap;
friend class MarkingBarrier;
friend class OldLargeObjectSpace;
template <typename ConcreteVisitor>
friend class MarkingVisitorBase;
friend class MarkCompactCollector;
friend class MemoryBalancer;
friend class MinorGCJob;
friend class MinorGCTaskObserver;
friend class MinorMarkSweepCollector;
friend class MinorMSIncrementalMarkingTaskObserver;
friend class NewLargeObjectSpace;
friend class NewSpace;
friend class ObjectStatsCollector;
friend class PageMetadata;
friend class PagedNewSpaceAllocatorPolicy;
friend class PagedSpaceAllocatorPolicy;
friend class PagedSpaceBase;
friend class PagedSpaceForNewSpace;
friend class PauseAllocationObserversScope;
friend class PretenuringHandler;
friend class ReadOnlyRoots;
friend class DisableConservativeStackScanningScopeForTesting;
friend class Scavenger;
friend class ScavengerCollector;
friend class ScavengerWeakObjectsProcessor;
friend class ScheduleMinorGCTaskObserver;
friend class SemiSpaceNewSpace;
friend class SemiSpaceNewSpaceAllocatorPolicy;
friend class StressConcurrentAllocationObserver;
friend class Space;
friend class SpaceWithLinearArea;
friend class Sweeper;
friend class UnifiedHeapMarkingState;
friend class heap::TestMemoryAllocatorScope;
friend class Factory;
friend class LocalFactory;
template <typename IsolateT>
friend class Deserializer;
friend class Isolate;
friend class heap::HeapTester;
FRIEND_TEST(SpacesTest, InlineAllocationObserverCadence);
FRIEND_TEST(SpacesTest, AllocationObserver);
friend class HeapInternalsBase;
};
constexpr const char* ToString(Heap::SweepingForcedFinalizationMode mode) {
switch (mode) {
case Heap::SweepingForcedFinalizationMode::kV8Only:
return "v8 only";
case Heap::SweepingForcedFinalizationMode::kUnifiedHeap:
return "unified heap";
}
}
#define DECL_RIGHT_TRIM(T) \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) void \
Heap::RightTrimArray<T>(Tagged<T> object, int new_capacity, \
int old_capacity);
RIGHT_TRIMMABLE_ARRAY_LIST(DECL_RIGHT_TRIM)
#undef DECL_RIGHT_TRIM
struct HexAddressTag;
using HexAddress = base::StrongAlias<HexAddressTag, Address>;
using ByteSize = ::heap::base::ByteSize;
#define CAGE_STATS_FIELDS(V) \
V(HexAddress, start) \
V(ByteSize, size) \
V(ByteSize, free_size) \
V(ByteSize, largest_free_region) \
V(base::BoundedPageAllocator::AllocationStatus, last_allocation_status)
class CageStats {
public:
#define DECL_FIELD(type, name) type name = {};
CAGE_STATS_FIELDS(DECL_FIELD)
#undef DECL_FIELD
};
using TraceRingBuffer = char[Heap::kTraceRingBufferSize + 1];
#define HEAP_STATS_FIELDS(V) \
V(ByteSize, ro_space_size) \
V(ByteSize, ro_space_capacity) \
V(ByteSize, new_space_size) \
V(ByteSize, new_space_capacity) \
V(ByteSize, old_space_size) \
V(ByteSize, old_space_capacity) \
V(ByteSize, code_space_size) \
V(ByteSize, code_space_capacity) \
V(ByteSize, map_space_size) \
V(ByteSize, map_space_capacity) \
V(ByteSize, lo_space_size) \
V(ByteSize, code_lo_space_size) \
V(size_t, global_handle_count) \
V(size_t, weak_global_handle_count) \
V(size_t, pending_global_handle_count) \
V(size_t, near_death_global_handle_count) \
V(size_t, free_global_handle_count) \
V(ByteSize, memory_allocator_size) \
V(ByteSize, memory_allocator_capacity) \
V(ByteSize, malloced_memory) \
V(ByteSize, malloced_peak_memory) \
V(size_t, isolate_count) \
V(size_t, last_os_error) \
V(bool, is_main_isolate) \
V(CageStats, main_cage) \
V(CageStats, trusted_cage) \
V(CageStats, code_cage) \
V(TraceRingBuffer, last_few_messages)
class HeapStats {
public:
static const int kStartMarker = 0xDECADE00;
static const int kEndMarker = 0xDECADE01;
intptr_t start_marker = 0;
#define DECL_FIELD(type, name) type name = {};
HEAP_STATS_FIELDS(DECL_FIELD)
#undef DECL_FIELD
intptr_t end_marker = 0;
};
class V8_NODISCARD AlwaysAllocateScope {
public:
inline ~AlwaysAllocateScope();
private:
friend class AlwaysAllocateScopeForTesting;
friend class Evacuator;
friend class Heap;
friend class HeapAllocator;
friend class Isolate;
friend class GlobalBackingStoreRegistry;
explicit inline AlwaysAllocateScope(Heap* heap);
Heap* heap_;
};
class V8_NODISCARD GCCallbacksScope final {
public:
explicit GCCallbacksScope(Heap* heap);
~GCCallbacksScope();
bool CheckReenter() const;
private:
Heap* const heap_;
};
class V8_NODISCARD AlwaysAllocateScopeForTesting {
public:
explicit inline AlwaysAllocateScopeForTesting(Heap* heap);
private:
AlwaysAllocateScope scope_;
};
class CodePageMemoryModificationScopeForDebugging {
public:
explicit CodePageMemoryModificationScopeForDebugging(
Heap* heap, VirtualMemory* reservation, base::AddressRegion region);
explicit CodePageMemoryModificationScopeForDebugging(
MemoryChunkMetadata* chunk);
~CodePageMemoryModificationScopeForDebugging();
private:
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || \
V8_HEAP_USE_PKU_JIT_WRITE_PROTECT || V8_HEAP_USE_BECORE_JIT_WRITE_PROTECT
RwxMemoryWriteScope rwx_write_scope_;
#endif
};
class V8_NODISCARD IgnoreLocalGCRequests {
public:
explicit inline IgnoreLocalGCRequests(Heap* heap);
inline ~IgnoreLocalGCRequests();
private:
Heap* heap_;
};
class V8_EXPORT_PRIVATE PagedSpaceIterator {
public:
explicit PagedSpaceIterator(const Heap* heap)
: heap_(heap), counter_(FIRST_GROWABLE_PAGED_SPACE) {}
PagedSpace* Next();
private:
const Heap* const heap_;
int counter_;
};
class V8_EXPORT_PRIVATE HeapObjectIterator {
public:
enum HeapObjectsFiltering { kNoFiltering, kFilterUnreachable };
explicit HeapObjectIterator(Heap* heap,
HeapObjectsFiltering filtering = kNoFiltering);
HeapObjectIterator(Heap* heap, const SafepointScope& safepoint_scope,
HeapObjectsFiltering filtering = kNoFiltering);
~HeapObjectIterator();
Tagged<HeapObject> Next();
private:
HeapObjectIterator(Heap* heap, SafepointScope* safepoint_scope_or_nullptr,
HeapObjectsFiltering filtering);
Tagged<HeapObject> NextObject();
Heap* heap_;
DISALLOW_GARBAGE_COLLECTION(no_heap_allocation_)
std::unique_ptr<SafepointScope> safepoint_scope_;
std::unique_ptr<HeapObjectsFilter> filter_;
SpaceIterator space_iterator_;
std::unique_ptr<ObjectIterator> object_iterator_;
};
class WeakObjectRetainer {
public:
virtual ~WeakObjectRetainer() = default;
virtual Tagged<Object> RetainAs(Tagged<Object> object) = 0;
virtual bool ShouldRecordSlots() const = 0;
virtual void RecordSlot(Tagged<HeapObject> host, ObjectSlot slot,
Tagged<HeapObject> object) = 0;
};
class HeapObjectAllocationTracker {
public:
virtual void AllocationEvent(Address addr, int size) = 0;
virtual void MoveEvent(Address from, Address to, int size) {}
virtual void UpdateObjectSizeEvent(Address addr, int size) {}
virtual ~HeapObjectAllocationTracker() = default;
};
template <typename T>
inline Tagged<T> ForwardingAddress(Tagged<T> heap_obj);
template <>
class StrongRootAllocator<Address> : public StrongRootAllocatorBase {
public:
using value_type = Address;
template <typename HeapOrIsolateT>
explicit StrongRootAllocator(HeapOrIsolateT* heap_or_isolate)
: StrongRootAllocatorBase(heap_or_isolate) {}
template <typename U>
StrongRootAllocator(const StrongRootAllocator<U>& other) V8_NOEXCEPT
: StrongRootAllocatorBase(other) {}
Address* allocate(size_t n) { return allocate_impl(n); }
void deallocate(Address* p, size_t n) noexcept {
return deallocate_impl(p, n);
}
};
class V8_EXPORT_PRIVATE V8_NODISCARD EmbedderStackStateScope final {
public:
EmbedderStackStateScope(Heap* heap, EmbedderStackStateOrigin origin,
StackState stack_state);
~EmbedderStackStateScope();
private:
Heap* const heap_;
const StackState old_stack_state_;
std::optional<EmbedderStackStateOrigin> old_origin_;
};
class V8_NODISCARD DisableConservativeStackScanningScopeForTesting {
public:
explicit inline DisableConservativeStackScanningScopeForTesting(Heap* heap)
: embedder_scope_(heap, EmbedderStackStateOrigin::kExplicitInvocation,
StackState::kNoHeapPointers) {}
private:
EmbedderStackStateScope embedder_scope_;
};
class V8_NODISCARD CppClassNamesAsHeapObjectNameScope final {
public:
explicit CppClassNamesAsHeapObjectNameScope(v8::CppHeap* heap);
~CppClassNamesAsHeapObjectNameScope();
private:
std::unique_ptr<cppgc::internal::ClassNameAsHeapObjectNameScope> scope_;
};
class ClearStaleLeftTrimmedPointerVisitor : public RootVisitor {
public:
ClearStaleLeftTrimmedPointerVisitor(Heap* heap, RootVisitor* visitor);
void VisitRootPointer(Root root, const char* description,
FullObjectSlot p) override;
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void VisitRunningCode(FullObjectSlot code_slot,
FullObjectSlot istream_or_smi_zero_slot) override;
void Synchronize(VisitorSynchronization::SyncTag tag) override {
visitor_->Synchronize(tag);
}
PtrComprCageBase cage_base() const {
#if V8_COMPRESS_POINTERS
return cage_base_;
#else
return PtrComprCageBase{};
#endif
}
private:
inline void ClearLeftTrimmedOrForward(Root root, const char* description,
FullObjectSlot p);
inline bool IsLeftTrimmed(FullObjectSlot p);
Heap* heap_;
RootVisitor* visitor_;
#if V8_COMPRESS_POINTERS
const PtrComprCageBase cage_base_;
#endif
};
}
}
#ifdef _LIBCPP_HAS_ASAN_CONTAINER_ANNOTATIONS_FOR_ALL_ALLOCATORS
template <typename T>
struct ::std::__asan_annotate_container_with_allocator<
v8::internal::StrongRootAllocator<T>> : ::std::false_type {};
#endif
#endif