1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/mem/linear_space.h"
17
18 #include "ecmascript/free_object.h"
19 #include "ecmascript/js_hclass-inl.h"
20 #include "ecmascript/mem/allocator-inl.h"
21 #include "ecmascript/mem/heap.h"
22 #include "ecmascript/mem/remembered_set.h"
23
24 namespace panda::ecmascript {
LinearSpace(Heap * heap,MemSpaceType type,size_t initialCapacity,size_t maximumCapacity)25 LinearSpace::LinearSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity)
26 : Space(heap, type, initialCapacity, maximumCapacity), waterLine_(0)
27 {
28 }
29
Allocate(size_t size,bool isPromoted)30 uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted)
31 {
32 auto object = allocator_->Allocate(size);
33 if (object != 0) {
34 return object;
35 }
36 if (Expand(isPromoted)) {
37 if (!isPromoted) {
38 heap_->TryTriggerConcurrentMarking();
39 }
40 object = allocator_->Allocate(size);
41 } else if (heap_->GetJSThread()->IsMarking()) {
42 // Temporary adjust semi space capacity
43 overShootSize_ = SEMI_SPACE_OVERSHOOT_SIZE;
44 if (Expand(isPromoted)) {
45 object = allocator_->Allocate(size);
46 }
47 }
48 return object;
49 }
50
Expand(bool isPromoted)51 bool LinearSpace::Expand(bool isPromoted)
52 {
53 if (committedSize_ >= maximumCapacity_ + overShootSize_) {
54 return false;
55 }
56
57 uintptr_t top = allocator_->GetTop();
58 auto currentRegion = GetCurrentRegion();
59 if (currentRegion != nullptr) {
60 if (!isPromoted) {
61 if (currentRegion->HasAgeMark()) {
62 allocateAfterLastGC_ +=
63 currentRegion->GetAllocatedBytes(top) - currentRegion->GetAllocatedBytes(waterLine_);
64 } else {
65 allocateAfterLastGC_ += currentRegion->GetAllocatedBytes(top);
66 }
67 } else {
68 // For GC
69 survivalObjectSize_ += currentRegion->GetAllocatedBytes(top);
70 }
71 currentRegion->SetHighWaterMark(top);
72 }
73 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE);
74 region->SetFlag(GetRegionFlag());
75 allocator_->Reset(region->GetBegin(), region->GetEnd());
76
77 AddRegion(region);
78 return true;
79 }
80
Stop()81 void LinearSpace::Stop()
82 {
83 if (GetCurrentRegion() != nullptr) {
84 GetCurrentRegion()->SetHighWaterMark(allocator_->GetTop());
85 }
86 }
87
SemiSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)88 SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
89 : LinearSpace(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity)
90 {
91 allocator_ = new BumpPointerAllocator();
92 }
93
Initialize()94 void SemiSpace::Initialize()
95 {
96 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE);
97 region->SetFlag(GetRegionFlag());
98 AddRegion(region);
99 allocator_->Reset(region->GetBegin(), region->GetEnd());
100 }
101
Restart()102 void SemiSpace::Restart()
103 {
104 overShootSize_ = 0;
105 survivalObjectSize_ = 0;
106 allocateAfterLastGC_ = 0;
107 Initialize();
108 }
109
AllocateSync(size_t size)110 uintptr_t SemiSpace::AllocateSync(size_t size)
111 {
112 os::memory::LockHolder lock(lock_);
113 return Allocate(size, true);
114 }
115
SwapRegion(Region * region,SemiSpace * fromSpace)116 bool SemiSpace::SwapRegion(Region *region, SemiSpace *fromSpace)
117 {
118 os::memory::LockHolder lock(lock_);
119 if (committedSize_ + region->GetCapacity() > maximumCapacity_ + overShootSize_) {
120 return false;
121 }
122 fromSpace->RemoveRegion(region);
123
124 region->SetFlag(RegionFlags::IS_IN_NEW_TO_NEW_SET);
125 region->SetSpace(this);
126
127 regionList_.AddNodeToFront(region);
128 IncrementCommitted(region->GetCapacity());
129 IncrementObjectSize(region->GetSize());
130 survivalObjectSize_ += region->GetAllocatedBytes();
131 return true;
132 }
133
SetWaterLine()134 void SemiSpace::SetWaterLine()
135 {
136 waterLine_ = allocator_->GetTop();
137 allocateAfterLastGC_ = 0;
138 Region *last = GetCurrentRegion();
139 if (last != nullptr) {
140 last->SetFlag(RegionFlags::HAS_AGE_MARK);
141
142 EnumerateRegions([&last](Region *current) {
143 if (current != last) {
144 current->SetFlag(RegionFlags::BELOW_AGE_MARK);
145 }
146 });
147 survivalObjectSize_ += last->GetAllocatedBytes(waterLine_);
148 }
149 }
150
GetHeapObjectSize() const151 size_t SemiSpace::GetHeapObjectSize() const
152 {
153 return survivalObjectSize_ + allocateAfterLastGC_;
154 }
155
GetSurvivalObjectSize() const156 size_t SemiSpace::GetSurvivalObjectSize() const
157 {
158 return survivalObjectSize_;
159 }
160
SetOverShootSize(size_t size)161 void SemiSpace::SetOverShootSize(size_t size)
162 {
163 overShootSize_ = size;
164 }
165
AdjustCapacity(size_t allocatedSizeSinceGC)166 bool SemiSpace::AdjustCapacity(size_t allocatedSizeSinceGC)
167 {
168 static constexpr double growObjectSurvivalRate = 0.8;
169 static constexpr double shrinkObjectSurvivalRate = 0.2;
170 static constexpr int growingFactor = 2;
171 if (allocatedSizeSinceGC <= maximumCapacity_ * growObjectSurvivalRate / growingFactor) {
172 return false;
173 }
174 double curObjectSurvivalRate = static_cast<double>(survivalObjectSize_) / allocatedSizeSinceGC;
175 if (curObjectSurvivalRate > growObjectSurvivalRate) {
176 size_t maxCapacity = heap_->GetEcmaVM()->GetJSOptions().MaxSemiSpaceCapacity();
177 if (GetMaximumCapacity() >= maxCapacity) {
178 return false;
179 }
180 size_t newCapacity = GetMaximumCapacity() * growingFactor;
181 SetMaximumCapacity(std::min(newCapacity, maxCapacity));
182 return true;
183 } else if (curObjectSurvivalRate < shrinkObjectSurvivalRate) {
184 if (GetMaximumCapacity() <= initialCapacity_) {
185 return false;
186 }
187 size_t newCapacity = GetMaximumCapacity() / growingFactor;
188 SetMaximumCapacity(std::max(newCapacity, initialCapacity_));
189 return true;
190 }
191 return false;
192 }
193
IterateOverObjects(const std::function<void (TaggedObject * object)> & visitor) const194 void SemiSpace::IterateOverObjects(const std::function<void(TaggedObject *object)> &visitor) const
195 {
196 auto current = GetCurrentRegion();
197 EnumerateRegions([&](Region *region) {
198 auto curPtr = region->GetBegin();
199 uintptr_t endPtr;
200 if (region == current) {
201 auto top = allocator_->GetTop();
202 endPtr = curPtr + region->GetAllocatedBytes(top);
203 } else {
204 endPtr = curPtr + region->GetAllocatedBytes();
205 }
206
207 size_t objSize;
208 while (curPtr < endPtr) {
209 auto freeObject = FreeObject::Cast(curPtr);
210 if (!freeObject->IsFreeObject()) {
211 auto obj = reinterpret_cast<TaggedObject *>(curPtr);
212 visitor(obj);
213 objSize = obj->GetClass()->SizeFromJSHClass(obj);
214 } else {
215 objSize = freeObject->Available();
216 }
217 curPtr += objSize;
218 CHECK_OBJECT_SIZE(objSize);
219 }
220 CHECK_REGION_END(curPtr, endPtr);
221 });
222 }
223
GetAllocatedSizeSinceGC() const224 size_t SemiSpace::GetAllocatedSizeSinceGC() const
225 {
226 size_t currentRegionSize = 0;
227 auto currentRegion = GetCurrentRegion();
228 if (currentRegion != nullptr) {
229 currentRegionSize = currentRegion->GetAllocatedBytes();
230 if (currentRegion->HasAgeMark()) {
231 currentRegionSize -= currentRegion->GetAllocatedBytes(waterLine_);
232 }
233 }
234 return allocateAfterLastGC_ + currentRegionSize;
235 }
236
SnapShotSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)237 SnapShotSpace::SnapShotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
238 : LinearSpace(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity)
239 {
240 }
241 } // namespace panda::ecmascript
242