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/concurrent_marker.h"
22 #include "ecmascript/mem/heap.h"
23 #include "ecmascript/mem/mem_controller.h"
24
25 namespace panda::ecmascript {
LinearSpace(Heap * heap,MemSpaceType type,size_t initialCapacity,size_t maximumCapacity)26 LinearSpace::LinearSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity)
27 : Space(heap, heap->GetHeapRegionAllocator(), type, initialCapacity, maximumCapacity),
28 waterLine_(0)
29 {
30 }
31
Allocate(size_t size,bool isPromoted)32 uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted)
33 {
34 auto object = allocator_.Allocate(size);
35 if (object != 0) {
36 #ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
37 // can not heap sampling in gc.
38 if (!isPromoted) {
39 InvokeAllocationInspector(object, size, size);
40 }
41 #endif
42 return object;
43 }
44 if (Expand(isPromoted)) {
45 if (!isPromoted && !heap_->NeedStopCollection()) {
46 heap_->TryTriggerIncrementalMarking();
47 heap_->TryTriggerIdleCollection();
48 heap_->TryTriggerConcurrentMarking();
49 }
50 object = allocator_.Allocate(size);
51 } else if (heap_->GetJSThread()->IsMarking() || !heap_->IsEmptyIdleTask()) {
52 // Temporary adjust semi space capacity
53 if (heap_->IsConcurrentFullMark()) {
54 overShootSize_ = heap_->GetOldSpace()->GetMaximumCapacity() - heap_->GetOldSpace()->GetInitialCapacity();
55 } else {
56 size_t stepOverShootSize = heap_->GetEcmaVM()->GetEcmaParamConfiguration().GetSemiSpaceStepOvershootSize();
57 size_t maxOverShootSize = std::max(initialCapacity_ / 2, stepOverShootSize); // 2: half
58 if (overShootSize_ < maxOverShootSize) {
59 overShootSize_ += stepOverShootSize;
60 }
61 }
62
63 if (Expand(isPromoted)) {
64 object = allocator_.Allocate(size);
65 }
66 }
67 #ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
68 if (object != 0 && !isPromoted) {
69 InvokeAllocationInspector(object, size, size);
70 }
71 #endif
72 return object;
73 }
74
Expand(bool isPromoted)75 bool LinearSpace::Expand(bool isPromoted)
76 {
77 if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_ &&
78 !heap_->NeedStopCollection()) {
79 return false;
80 }
81
82 uintptr_t top = allocator_.GetTop();
83 auto currentRegion = GetCurrentRegion();
84 if (currentRegion != nullptr) {
85 if (!isPromoted) {
86 if (currentRegion->HasAgeMark()) {
87 allocateAfterLastGC_ +=
88 currentRegion->GetAllocatedBytes(top) - currentRegion->GetAllocatedBytes(waterLine_);
89 } else {
90 allocateAfterLastGC_ += currentRegion->GetAllocatedBytes(top);
91 }
92 } else {
93 // For GC
94 survivalObjectSize_ += currentRegion->GetAllocatedBytes(top);
95 }
96 currentRegion->SetHighWaterMark(top);
97 }
98 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE, heap_->GetJSThread());
99 allocator_.Reset(region->GetBegin(), region->GetEnd());
100
101 AddRegion(region);
102 return true;
103 }
104
Stop()105 void LinearSpace::Stop()
106 {
107 if (GetCurrentRegion() != nullptr) {
108 GetCurrentRegion()->SetHighWaterMark(allocator_.GetTop());
109 }
110 }
111
ResetAllocator()112 void LinearSpace::ResetAllocator()
113 {
114 auto currentRegion = GetCurrentRegion();
115 if (currentRegion != nullptr) {
116 allocator_.Reset(currentRegion->GetBegin(), currentRegion->GetEnd(), currentRegion->GetHighWaterMark());
117 }
118 }
119
IterateOverObjects(const std::function<void (TaggedObject * object)> & visitor) const120 void LinearSpace::IterateOverObjects(const std::function<void(TaggedObject *object)> &visitor) const
121 {
122 auto current = GetCurrentRegion();
123 EnumerateRegions([&](Region *region) {
124 auto curPtr = region->GetBegin();
125 uintptr_t endPtr = 0;
126 if (region == current) {
127 auto top = allocator_.GetTop();
128 endPtr = curPtr + region->GetAllocatedBytes(top);
129 } else {
130 endPtr = curPtr + region->GetAllocatedBytes();
131 }
132
133 size_t objSize;
134 while (curPtr < endPtr) {
135 auto freeObject = FreeObject::Cast(curPtr);
136 // If curPtr is freeObject, It must to mark unpoison first.
137 ASAN_UNPOISON_MEMORY_REGION(freeObject, TaggedObject::TaggedObjectSize());
138 if (!freeObject->IsFreeObject()) {
139 auto obj = reinterpret_cast<TaggedObject *>(curPtr);
140 visitor(obj);
141 objSize = obj->GetClass()->SizeFromJSHClass(obj);
142 } else {
143 freeObject->AsanUnPoisonFreeObject();
144 objSize = freeObject->Available();
145 freeObject->AsanPoisonFreeObject();
146 }
147 curPtr += objSize;
148 CHECK_OBJECT_SIZE(objSize);
149 }
150 CHECK_REGION_END(curPtr, endPtr);
151 });
152 }
153
InvokeAllocationInspector(Address object,size_t size,size_t alignedSize)154 void LinearSpace::InvokeAllocationInspector(Address object, size_t size, size_t alignedSize)
155 {
156 ASSERT(size <= alignedSize);
157 if (LIKELY(!allocationCounter_.IsActive())) {
158 return;
159 }
160 if (alignedSize >= allocationCounter_.NextBytes()) {
161 allocationCounter_.InvokeAllocationInspector(object, size, alignedSize);
162 }
163 allocationCounter_.AdvanceAllocationInspector(alignedSize);
164 }
165
SemiSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)166 SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
167 : LinearSpace(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity),
168 minimumCapacity_(initialCapacity) {}
169
Initialize()170 void SemiSpace::Initialize()
171 {
172 Region *region = heapRegionAllocator_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE, heap_->GetJSThread());
173 AddRegion(region);
174 allocator_.Reset(region->GetBegin(), region->GetEnd());
175 }
176
Restart()177 void SemiSpace::Restart()
178 {
179 overShootSize_ = 0;
180 survivalObjectSize_ = 0;
181 allocateAfterLastGC_ = 0;
182 Initialize();
183 }
184
AllocateSync(size_t size)185 uintptr_t SemiSpace::AllocateSync(size_t size)
186 {
187 LockHolder lock(lock_);
188 return Allocate(size, true);
189 }
190
SwapRegion(Region * region,SemiSpace * fromSpace)191 bool SemiSpace::SwapRegion(Region *region, SemiSpace *fromSpace)
192 {
193 LockHolder lock(lock_);
194 if (committedSize_ + region->GetCapacity() > maximumCapacity_ + overShootSize_) {
195 return false;
196 }
197 fromSpace->RemoveRegion(region);
198
199 region->SetGCFlag(RegionGCFlags::IN_NEW_TO_NEW_SET);
200
201 if (UNLIKELY(heap_->ShouldVerifyHeap())) {
202 region->ResetInactiveSemiSpace();
203 }
204
205 regionList_.AddNodeToFront(region);
206 IncreaseCommitted(region->GetCapacity());
207 IncreaseObjectSize(region->GetSize());
208 survivalObjectSize_ += region->GetAllocatedBytes();
209 return true;
210 }
211
SetWaterLine()212 void SemiSpace::SetWaterLine()
213 {
214 waterLine_ = allocator_.GetTop();
215 allocateAfterLastGC_ = 0;
216 Region *last = GetCurrentRegion();
217 if (last != nullptr) {
218 last->SetGCFlag(RegionGCFlags::HAS_AGE_MARK);
219
220 EnumerateRegions([&last](Region *current) {
221 if (current != last) {
222 current->SetGCFlag(RegionGCFlags::BELOW_AGE_MARK);
223 }
224 });
225 survivalObjectSize_ += last->GetAllocatedBytes(waterLine_);
226 } else {
227 LOG_GC(INFO) << "SetWaterLine: No region survival in current gc, current region available size: "
228 << allocator_.Available();
229 }
230 }
231
SetWaterLineWithoutGC()232 void SemiSpace::SetWaterLineWithoutGC()
233 {
234 waterLine_ = allocator_.GetTop();
235 Region *last = GetCurrentRegion();
236 if (last != nullptr) {
237 last->SetGCFlag(RegionGCFlags::HAS_AGE_MARK);
238
239 EnumerateRegions([&last](Region *current) {
240 if (current != last) {
241 current->SetGCFlag(RegionGCFlags::BELOW_AGE_MARK);
242 }
243 });
244 survivalObjectSize_ += allocateAfterLastGC_;
245 }
246 allocateAfterLastGC_ = 0;
247 }
248
GetHeapObjectSize() const249 size_t SemiSpace::GetHeapObjectSize() const
250 {
251 return survivalObjectSize_ + allocateAfterLastGC_;
252 }
253
GetSurvivalObjectSize() const254 size_t SemiSpace::GetSurvivalObjectSize() const
255 {
256 return survivalObjectSize_;
257 }
258
SetOverShootSize(size_t size)259 void SemiSpace::SetOverShootSize(size_t size)
260 {
261 overShootSize_ = size;
262 }
263
AdjustCapacity(size_t allocatedSizeSinceGC)264 bool SemiSpace::AdjustCapacity(size_t allocatedSizeSinceGC)
265 {
266 if (allocatedSizeSinceGC <= initialCapacity_ * GROW_OBJECT_SURVIVAL_RATE / GROWING_FACTOR) {
267 return false;
268 }
269 double curObjectSurvivalRate = static_cast<double>(survivalObjectSize_) / allocatedSizeSinceGC;
270 double initialObjectRate = static_cast<double>(survivalObjectSize_) / initialCapacity_;
271 if (curObjectSurvivalRate > GROW_OBJECT_SURVIVAL_RATE || initialObjectRate > GROW_OBJECT_SURVIVAL_RATE) {
272 if (initialCapacity_ >= maximumCapacity_) {
273 return false;
274 }
275 size_t newCapacity = initialCapacity_ * GROWING_FACTOR;
276 SetInitialCapacity(std::min(newCapacity, maximumCapacity_));
277 if (newCapacity == maximumCapacity_) {
278 heap_->GetEcmaVM()->GetJSObjectResizingStrategy()->UpdateGrowStep(
279 heap_->GetJSThread(),
280 JSObjectResizingStrategy::PROPERTIES_GROW_SIZE * 2); // 2: double
281 }
282 return true;
283 } else if (curObjectSurvivalRate < SHRINK_OBJECT_SURVIVAL_RATE) {
284 if (initialCapacity_ <= minimumCapacity_) {
285 return false;
286 }
287 double speed = heap_->GetMemController()->GetNewSpaceAllocationThroughputPerMS();
288 if (speed > LOW_ALLOCATION_SPEED_PER_MS) {
289 return false;
290 }
291 size_t newCapacity = initialCapacity_ / GROWING_FACTOR;
292 SetInitialCapacity(std::max(newCapacity, minimumCapacity_));
293 heap_->GetEcmaVM()->GetJSObjectResizingStrategy()->UpdateGrowStep(heap_->GetJSThread());
294 return true;
295 }
296 return false;
297 }
298
GetAllocatedSizeSinceGC(uintptr_t top) const299 size_t SemiSpace::GetAllocatedSizeSinceGC(uintptr_t top) const
300 {
301 size_t currentRegionSize = 0;
302 auto currentRegion = GetCurrentRegion();
303 if (currentRegion != nullptr) {
304 currentRegionSize = currentRegion->GetAllocatedBytes(top);
305 if (currentRegion->HasAgeMark()) {
306 currentRegionSize -= currentRegion->GetAllocatedBytes(waterLine_);
307 }
308 }
309 return allocateAfterLastGC_ + currentRegionSize;
310 }
311
SnapshotSpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)312 SnapshotSpace::SnapshotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
313 : LinearSpace(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity) {}
314
ReadOnlySpace(Heap * heap,size_t initialCapacity,size_t maximumCapacity)315 ReadOnlySpace::ReadOnlySpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity)
316 : LinearSpace(heap, MemSpaceType::READ_ONLY_SPACE, initialCapacity, maximumCapacity) {}
317 } // namespace panda::ecmascript
318