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
23 namespace panda::ecmascript {
LinearSpace(Heap * heap,MemSpaceType type,size_t initialCapacity,size_t maximumCapacity)24 LinearSpace::LinearSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity)
25 : Space(heap->GetHeapRegionAllocator(), type, initialCapacity, maximumCapacity),
26 heap_(heap),
27 waterLine_(0)
28 {
29 }
30
Allocate(size_t size,bool isPromoted)31 uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted)
32 {
33 auto object = allocator_.Allocate(size);
34 if (object != 0) {
35 return object;
36 }
37 if (Expand(isPromoted)) {
38 if (!isPromoted) {
39 heap_->TryTriggerConcurrentMarking();
40 }
41 object = allocator_.Allocate(size);
42 } else if (heap_->GetJSThread()->IsMarking()) {
43 // Temporary adjust semi space capacity
44 overShootSize_ = heap_->GetEcmaVM()->GetEcmaParamConfiguration().GetSemiSpaceOvershootSize();
45 if (Expand(isPromoted)) {
46 object = allocator_.Allocate(size);
47 }
48 }
49 return object;
50 }
51
Expand(bool isPromoted)52 bool LinearSpace::Expand(bool isPromoted)
53 {
54 if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_) {
55 return false;
56 }
57
58 uintptr_t top = allocator_.GetTop();
59 auto currentRegion = GetCurrentRegion();
60 if (currentRegion != nullptr) {
61 if (!isPromoted) {
62 if (currentRegion->HasAgeMark()) {
63 allocateAfterLastGC_ +=
64 currentRegion->GetAllocatedBytes(top) - currentRegion->GetAllocatedBytes(waterLine_);
65 } else {
66 allocateAfterLastGC_ += currentRegion->GetAllocatedBytes(top);
67 }
68 } else {
69 // For GC
70 survivalObjectSize_ += currentRegion->GetAllocatedBytes(top);
71 }
72 currentRegion->SetHighWaterMark(top);
73 }
74 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE, heap_->GetJSThread());
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
ResetAllocator()88 void LinearSpace::ResetAllocator()
89 {
90 auto currentRegion = GetCurrentRegion();
91 if (currentRegion != nullptr) {
92 allocator_.Reset(currentRegion->GetBegin(), currentRegion->GetEnd(), currentRegion->GetHighWaterMark());
93 }
94 }
95
IterateOverObjects(const std::function<void (TaggedObject * object)> & visitor) const96 void LinearSpace::IterateOverObjects(const std::function<void(TaggedObject *object)> &visitor) const
97 {
98 auto current = GetCurrentRegion();
99 EnumerateRegions([&](Region *region) {
100 auto curPtr = region->GetBegin();
101 uintptr_t endPtr = 0;
102 if (region == current) {
103 auto top = allocator_.GetTop();
104 endPtr = curPtr + region->GetAllocatedBytes(top);
105 } else {
106 endPtr = curPtr + region->GetAllocatedBytes();
107 }
108
109 size_t objSize;
110 while (curPtr < endPtr) {
111 auto freeObject = FreeObject::Cast(curPtr);
112 // If curPtr is freeObject, It must to mark unpoison first.
113 ASAN_UNPOISON_MEMORY_REGION(freeObject, TaggedObject::TaggedObjectSize());
114 if (!freeObject->IsFreeObject()) {
115 auto obj = reinterpret_cast<TaggedObject *>(curPtr);
116 visitor(obj);
117 objSize = obj->GetClass()->SizeFromJSHClass(obj);
118 } else {
119 freeObject->AsanUnPoisonFreeObject();
120 objSize = freeObject->Available();
121 freeObject->AsanPoisonFreeObject();
122 }
123 curPtr += objSize;
124 CHECK_OBJECT_SIZE(objSize);
125 }
126 CHECK_REGION_END(curPtr, endPtr);
127 });
128 }
129
SemiSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)130 SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
131 : LinearSpace(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity),
132 minimumCapacity_(initialCapacity), newSpaceNativeLimit_(initialCapacity) {}
133
Initialize()134 void SemiSpace::Initialize()
135 {
136 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE, heap_->GetJSThread());
137 AddRegion(region);
138 allocator_.Reset(region->GetBegin(), region->GetEnd());
139 }
140
Restart()141 void SemiSpace::Restart()
142 {
143 overShootSize_ = 0;
144 survivalObjectSize_ = 0;
145 allocateAfterLastGC_ = 0;
146 Initialize();
147 }
148
AllocateSync(size_t size)149 uintptr_t SemiSpace::AllocateSync(size_t size)
150 {
151 os::memory::LockHolder lock(lock_);
152 return Allocate(size, true);
153 }
154
SwapRegion(Region * region,SemiSpace * fromSpace)155 bool SemiSpace::SwapRegion(Region *region, SemiSpace *fromSpace)
156 {
157 os::memory::LockHolder lock(lock_);
158 if (committedSize_ + region->GetCapacity() > maximumCapacity_ + overShootSize_) {
159 return false;
160 }
161 fromSpace->RemoveRegion(region);
162
163 region->SetGCFlag(RegionGCFlags::IN_NEW_TO_NEW_SET);
164
165 regionList_.AddNodeToFront(region);
166 IncreaseCommitted(region->GetCapacity());
167 IncreaseObjectSize(region->GetSize());
168 survivalObjectSize_ += region->GetAllocatedBytes();
169 return true;
170 }
171
SetWaterLine()172 void SemiSpace::SetWaterLine()
173 {
174 waterLine_ = allocator_.GetTop();
175 allocateAfterLastGC_ = 0;
176 Region *last = GetCurrentRegion();
177 if (last != nullptr) {
178 last->SetGCFlag(RegionGCFlags::HAS_AGE_MARK);
179
180 EnumerateRegions([&last](Region *current) {
181 if (current != last) {
182 current->SetGCFlag(RegionGCFlags::BELOW_AGE_MARK);
183 }
184 });
185 survivalObjectSize_ += last->GetAllocatedBytes(waterLine_);
186 }
187 }
188
GetHeapObjectSize() const189 size_t SemiSpace::GetHeapObjectSize() const
190 {
191 return survivalObjectSize_ + allocateAfterLastGC_;
192 }
193
GetSurvivalObjectSize() const194 size_t SemiSpace::GetSurvivalObjectSize() const
195 {
196 return survivalObjectSize_;
197 }
198
SetOverShootSize(size_t size)199 void SemiSpace::SetOverShootSize(size_t size)
200 {
201 overShootSize_ = size;
202 }
203
AdjustCapacity(size_t allocatedSizeSinceGC)204 bool SemiSpace::AdjustCapacity(size_t allocatedSizeSinceGC)
205 {
206 if (allocatedSizeSinceGC <= initialCapacity_ * GROW_OBJECT_SURVIVAL_RATE / GROWING_FACTOR) {
207 return false;
208 }
209 double curObjectSurvivalRate = static_cast<double>(survivalObjectSize_) / allocatedSizeSinceGC;
210 if (curObjectSurvivalRate > GROW_OBJECT_SURVIVAL_RATE) {
211 if (initialCapacity_ >= maximumCapacity_) {
212 return false;
213 }
214 size_t newCapacity = initialCapacity_ * GROWING_FACTOR;
215 SetInitialCapacity(std::min(newCapacity, maximumCapacity_));
216 return true;
217 } else if (curObjectSurvivalRate < SHRINK_OBJECT_SURVIVAL_RATE) {
218 if (initialCapacity_ <= minimumCapacity_) {
219 return false;
220 }
221 size_t newCapacity = initialCapacity_ / GROWING_FACTOR;
222 SetInitialCapacity(std::max(newCapacity, minimumCapacity_));
223 return true;
224 }
225 return false;
226 }
227
AdjustNativeLimit(size_t previousNativeSize)228 void SemiSpace::AdjustNativeLimit(size_t previousNativeSize)
229 {
230 if (newSpaceNativeBindingSize_ <= newSpaceNativeLimit_ * GROW_OBJECT_SURVIVAL_RATE / GROWING_FACTOR) {
231 return;
232 }
233 double curObjectSurvivalRate = static_cast<double>(previousNativeSize) / newSpaceNativeBindingSize_;
234 if (curObjectSurvivalRate > GROW_OBJECT_SURVIVAL_RATE) {
235 if (newSpaceNativeLimit_ >= maximumCapacity_) {
236 return;
237 }
238 size_t newCapacity = newSpaceNativeLimit_ * GROWING_FACTOR;
239 newSpaceNativeLimit_ = std::min(newCapacity, maximumCapacity_);
240 } else if (curObjectSurvivalRate < SHRINK_OBJECT_SURVIVAL_RATE) {
241 if (newSpaceNativeLimit_ <= minimumCapacity_) {
242 return;
243 }
244 size_t newCapacity = newSpaceNativeLimit_ / GROWING_FACTOR;
245 newSpaceNativeLimit_ = std::max(newCapacity, minimumCapacity_);
246 }
247 }
248
GetAllocatedSizeSinceGC(uintptr_t top) const249 size_t SemiSpace::GetAllocatedSizeSinceGC(uintptr_t top) const
250 {
251 size_t currentRegionSize = 0;
252 auto currentRegion = GetCurrentRegion();
253 if (currentRegion != nullptr) {
254 currentRegionSize = currentRegion->GetAllocatedBytes(top);
255 if (currentRegion->HasAgeMark()) {
256 currentRegionSize -= currentRegion->GetAllocatedBytes(waterLine_);
257 }
258 }
259 return allocateAfterLastGC_ + currentRegionSize;
260 }
261
SnapshotSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)262 SnapshotSpace::SnapshotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
263 : LinearSpace(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity) {}
264
ReadOnlySpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)265 ReadOnlySpace::ReadOnlySpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
266 : LinearSpace(heap, MemSpaceType::READ_ONLY_SPACE, initialCapacity, maximumCapacity) {}
267 } // namespace panda::ecmascript
268