1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/cppgc/heap-growing.h"
6
7 #include <cmath>
8 #include <memory>
9
10 #include "include/cppgc/platform.h"
11 #include "src/base/macros.h"
12 #include "src/heap/cppgc/globals.h"
13 #include "src/heap/cppgc/heap.h"
14 #include "src/heap/cppgc/incremental-marking-schedule.h"
15 #include "src/heap/cppgc/stats-collector.h"
16 #include "src/heap/cppgc/task-handle.h"
17
18 namespace cppgc {
19 namespace internal {
20
21 namespace {
22 // Minimum ratio between limit for incremental GC and limit for atomic GC
23 // (to guarantee that limits are not to close to each other).
24 constexpr double kMaximumLimitRatioForIncrementalGC = 0.9;
25 // Minimum ratio between limit for incremental GC and limit for atomic GC
26 // (to guarantee that limit is not too close to current allocated size).
27 constexpr double kMinimumLimitRatioForIncrementalGC = 0.5;
28 } // namespace
29
30 class HeapGrowing::HeapGrowingImpl final
31 : public StatsCollector::AllocationObserver {
32 public:
33 HeapGrowingImpl(GarbageCollector*, StatsCollector*,
34 cppgc::Heap::ResourceConstraints);
35 ~HeapGrowingImpl();
36
37 HeapGrowingImpl(const HeapGrowingImpl&) = delete;
38 HeapGrowingImpl& operator=(const HeapGrowingImpl&) = delete;
39
40 void AllocatedObjectSizeIncreased(size_t) final;
41 // Only trigger GC on growing.
AllocatedObjectSizeDecreased(size_t)42 void AllocatedObjectSizeDecreased(size_t) final {}
43 void ResetAllocatedObjectSize(size_t) final;
44
limit_for_atomic_gc() const45 size_t limit_for_atomic_gc() const { return limit_for_atomic_gc_; }
limit_for_incremental_gc() const46 size_t limit_for_incremental_gc() const { return limit_for_incremental_gc_; }
47
48 void DisableForTesting();
49
50 private:
51 void ConfigureLimit(size_t allocated_object_size);
52
53 GarbageCollector* collector_;
54 StatsCollector* stats_collector_;
55 // Allow 1 MB heap by default;
56 size_t initial_heap_size_ = 1 * kMB;
57 size_t limit_for_atomic_gc_ = 0; // See ConfigureLimit().
58 size_t limit_for_incremental_gc_ = 0; // See ConfigureLimit().
59
60 SingleThreadedHandle gc_task_handle_;
61
62 bool disabled_for_testing_ = false;
63 };
64
HeapGrowingImpl(GarbageCollector * collector,StatsCollector * stats_collector,cppgc::Heap::ResourceConstraints constraints)65 HeapGrowing::HeapGrowingImpl::HeapGrowingImpl(
66 GarbageCollector* collector, StatsCollector* stats_collector,
67 cppgc::Heap::ResourceConstraints constraints)
68 : collector_(collector),
69 stats_collector_(stats_collector),
70 gc_task_handle_(SingleThreadedHandle::NonEmptyTag{}) {
71 if (constraints.initial_heap_size_bytes > 0) {
72 initial_heap_size_ = constraints.initial_heap_size_bytes;
73 }
74 constexpr size_t kNoAllocatedBytes = 0;
75 ConfigureLimit(kNoAllocatedBytes);
76 stats_collector->RegisterObserver(this);
77 }
78
~HeapGrowingImpl()79 HeapGrowing::HeapGrowingImpl::~HeapGrowingImpl() {
80 stats_collector_->UnregisterObserver(this);
81 }
82
AllocatedObjectSizeIncreased(size_t)83 void HeapGrowing::HeapGrowingImpl::AllocatedObjectSizeIncreased(size_t) {
84 if (disabled_for_testing_) return;
85 size_t allocated_object_size = stats_collector_->allocated_object_size();
86 if (allocated_object_size > limit_for_atomic_gc_) {
87 collector_->CollectGarbage(
88 GarbageCollector::Config::ConservativeAtomicConfig());
89 } else if (allocated_object_size > limit_for_incremental_gc_) {
90 collector_->StartIncrementalGarbageCollection(
91 GarbageCollector::Config::ConservativeIncrementalConfig());
92 }
93 }
94
ResetAllocatedObjectSize(size_t allocated_object_size)95 void HeapGrowing::HeapGrowingImpl::ResetAllocatedObjectSize(
96 size_t allocated_object_size) {
97 ConfigureLimit(allocated_object_size);
98 }
99
ConfigureLimit(size_t allocated_object_size)100 void HeapGrowing::HeapGrowingImpl::ConfigureLimit(
101 size_t allocated_object_size) {
102 const size_t size = std::max(allocated_object_size, initial_heap_size_);
103 limit_for_atomic_gc_ = std::max(static_cast<size_t>(size * kGrowingFactor),
104 size + kMinLimitIncrease);
105 // Estimate when to start incremental GC based on current allocation speed.
106 // Ideally we start incremental GC such that it is ready to finalize no
107 // later than when we reach |limit_for_atomic_gc_|. However, we need to cap
108 // |limit_for_incremental_gc_| within a range to prevent:
109 // 1) |limit_for_incremental_gc_| being too close to |limit_for_atomic_gc_|
110 // such that incremental gc gets nothing done before reaching
111 // |limit_for_atomic_gc_| (in case where the allocation rate is very low).
112 // 2) |limit_for_incremental_gc_| being too close to |size| such that GC is
113 // essentially always running and write barriers are always active (in
114 // case allocation rate is very high).
115 size_t estimated_bytes_allocated_during_incremental_gc =
116 std::ceil(IncrementalMarkingSchedule::kEstimatedMarkingTimeMs *
117 stats_collector_->GetRecentAllocationSpeedInBytesPerMs());
118 size_t limit_incremental_gc_based_on_allocation_rate =
119 limit_for_atomic_gc_ - estimated_bytes_allocated_during_incremental_gc;
120 size_t maximum_limit_incremental_gc =
121 size + (limit_for_atomic_gc_ - size) * kMaximumLimitRatioForIncrementalGC;
122 size_t minimum_limit_incremental_gc =
123 size + (limit_for_atomic_gc_ - size) * kMinimumLimitRatioForIncrementalGC;
124 limit_for_incremental_gc_ =
125 std::max(minimum_limit_incremental_gc,
126 std::min(maximum_limit_incremental_gc,
127 limit_incremental_gc_based_on_allocation_rate));
128 }
129
DisableForTesting()130 void HeapGrowing::HeapGrowingImpl::DisableForTesting() {
131 disabled_for_testing_ = true;
132 }
133
HeapGrowing(GarbageCollector * collector,StatsCollector * stats_collector,cppgc::Heap::ResourceConstraints constraints)134 HeapGrowing::HeapGrowing(GarbageCollector* collector,
135 StatsCollector* stats_collector,
136 cppgc::Heap::ResourceConstraints constraints)
137 : impl_(std::make_unique<HeapGrowing::HeapGrowingImpl>(
138 collector, stats_collector, constraints)) {}
139
140 HeapGrowing::~HeapGrowing() = default;
141
limit_for_atomic_gc() const142 size_t HeapGrowing::limit_for_atomic_gc() const {
143 return impl_->limit_for_atomic_gc();
144 }
limit_for_incremental_gc() const145 size_t HeapGrowing::limit_for_incremental_gc() const {
146 return impl_->limit_for_incremental_gc();
147 }
148
DisableForTesting()149 void HeapGrowing::DisableForTesting() { impl_->DisableForTesting(); }
150
151 // static
152 constexpr double HeapGrowing::kGrowingFactor;
153
154 } // namespace internal
155 } // namespace cppgc
156