• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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