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