/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/mem/space-inl.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/mem/heap.h" #include "ecmascript/mem/mem_controller.h" #include "ecmascript/mem/region-inl.h" #include "ecmascript/mem/region_factory.h" #include "ecmascript/mem/remembered_set.h" #include "libpandabase/utils/logger.h" namespace panda::ecmascript { Space::Space(Heap *heap, MemSpaceType spaceType, size_t initialCapacity, size_t maximumCapacity) : heap_(heap), vm_(heap_->GetEcmaVM()), thread_(vm_->GetJSThread()), regionFactory_(vm_->GetRegionFactory()), spaceType_(spaceType), initialCapacity_(initialCapacity), maximumCapacity_(maximumCapacity), committedSize_(0) { } void Space::AddRegion(Region *region) { LOG_ECMA_MEM(DEBUG) << "Add region:" << region << " to " << ToSpaceTypeName(spaceType_); regionList_.AddNode(region); IncrementCommitted(region->GetCapacity()); } void Space::AddRegionToFirst(Region *region) { LOG_ECMA_MEM(DEBUG) << "Add region to first:" << region << " to " << ToSpaceTypeName(spaceType_); regionList_.AddNodeToFirst(region); IncrementCommitted(region->GetCapacity()); } void Space::RemoveRegion(Region *region) { LOG_ECMA_MEM(DEBUG) << "Remove region:" << region << " to " << ToSpaceTypeName(spaceType_); if (regionList_.HasNode(region)) { if (spaceType_ == MemSpaceType::OLD_SPACE || spaceType_ == MemSpaceType::NON_MOVABLE || spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { region->RebuildKind(); } regionList_.RemoveNode(region); DecrementCommitted(region->GetCapacity()); } } void Space::Initialize() { Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); if (spaceType_ == MemSpaceType::SEMI_SPACE) { region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); } else if (spaceType_ == MemSpaceType::SNAPSHOT_SPACE) { region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); } else if (spaceType_ == MemSpaceType::OLD_SPACE) { region->InitializeKind(); region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); } else if (spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { region->InitializeKind(); region->SetFlag(RegionFlags::IS_IN_NON_MOVABLE_GENERATION); int res = region->SetCodeExecutableAndReadable(); LOG_ECMA_MEM(DEBUG) << "Initialize SetCodeExecutableAndReadable" << res; } else if (spaceType_ == MemSpaceType::NON_MOVABLE) { region->InitializeKind(); region->SetFlag(RegionFlags::IS_IN_NON_MOVABLE_GENERATION); } AddRegion(region); } void Space::ReclaimRegions() { EnumerateRegions([this](Region *current) { ClearAndFreeRegion(current); }); regionList_.Clear(); committedSize_ = 0; } void Space::ClearAndFreeRegion(Region *region) { LOG_ECMA_MEM(DEBUG) << "Clear region from:" << region << " to " << ToSpaceTypeName(spaceType_); region->ClearMarkBitmap(); region->ClearCrossRegionRememberedSet(); region->ClearOldToNewRememberedSet(); DecrementCommitted(region->GetCapacity()); if (spaceType_ == MemSpaceType::OLD_SPACE || spaceType_ == MemSpaceType::NON_MOVABLE || spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { region->DestroyKind(); } regionFactory_->FreeRegion(region); } size_t Space::GetHeapObjectSize() const { Region *last = GetCurrentRegion(); auto top = heap_->GetHeapManager()->GetNewSpaceAllocator().GetTop(); size_t result = last->GetAllocatedBytes(top); EnumerateRegions([&result, &last](Region *current) { if (current != last) { result += current->GetAllocatedBytes(); } }); return result; } SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity), ageMark_(0) { } bool SemiSpace::Expand(uintptr_t top) { if (committedSize_ >= maximumCapacity_) { return false; } GetCurrentRegion()->SetHighWaterMark(top); Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } AddRegion(region); return true; } bool SemiSpace::AddRegionToList(Region *region) { ASSERT(region != nullptr); if (GetCommittedSize() >= GetMaximumCapacity()) { return false; } region->ResetFlag(); region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); region->SetFlag(RegionFlags::IS_IN_PROMOTE_SET); region->SetSpace(this); AddRegionToFirst(region); return true; } void SemiSpace::Swap([[maybe_unused]] SemiSpace *other) {} void SemiSpace::SetAgeMark(uintptr_t mark) { ageMark_ = mark; Region *last = GetCurrentRegion(); last->SetFlag(RegionFlags::HAS_AGE_MARK); EnumerateRegions([&last](Region *current) { if (current != last) { current->SetFlag(RegionFlags::BELOW_AGE_MARK); } }); } bool SemiSpace::ContainObject(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { return true; } region = region->GetNext(); } return false; } bool SemiSpace::IsLive(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { auto freeObject = FreeObject::Cast(ToUintPtr(object)); return !freeObject->IsFreeObject(); } region = region->GetNext(); } return false; } void SemiSpace::IterateOverObjects(const std::function &visitor) const { auto current = GetCurrentRegion(); EnumerateRegions([&](Region *region) { auto curPtr = region->GetBegin(); uintptr_t endPtr; if (region == current) { auto top = heap_->GetHeapManager()->GetNewSpaceAllocator().GetTop(); endPtr = curPtr + region->GetAllocatedBytes(top); } else { endPtr = curPtr + region->GetAllocatedBytes(); } size_t objSize; while (curPtr < endPtr) { auto freeObject = FreeObject::Cast(curPtr); if (!freeObject->IsFreeObject()) { auto obj = reinterpret_cast(curPtr); visitor(obj); objSize = obj->GetClass()->SizeFromJSHClass(obj); } else { objSize = freeObject->Available(); } curPtr += objSize; CHECK_OBJECT_SIZE(objSize); } CHECK_REGION_END(curPtr, endPtr); }); } size_t SemiSpace::GetAllocatedSizeSinceGC() const { Region *last = GetCurrentRegion(); if (last->BelowAgeMark()) { return 0; } auto top = GetHeap()->GetHeapManager()->GetNewSpaceAllocator().GetTop(); size_t result = 0; if (last->HasAgeMark()) { result = last->GetAllocatedBytes(top) - last->GetAllocatedBytes(ageMark_); return result; } else { result = last->GetAllocatedBytes(top); } EnumerateRegions([this, &result, &last](Region *current) { if (!current->HasAgeMark() && !current->BelowAgeMark()) { if (current != last) { result += current->GetAllocatedBytes(); } } else if (current->HasAgeMark()) { result += current->GetAllocatedBytes(); result -= current->GetAllocatedBytes(this->GetAgeMark()); } }); return result; } OldSpace::OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::OLD_SPACE, initialCapacity, maximumCapacity) { } bool OldSpace::Expand() { if (committedSize_ >= maximumCapacity_) { LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of old space is too big. "; return false; } Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } region->InitializeKind(); AddRegion(region); return true; } bool OldSpace::AddRegionToList(Region *region) { ASSERT(region != nullptr); if (GetCommittedSize() >= GetMaximumCapacity()) { LOG_ECMA_MEM(FATAL) << "Committed size " << GetCommittedSize() << " of old space is too big. "; return false; } if (!region->InOldGeneration()) { region->InitializeKind(); } region->ResetFlag(); region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); region->SetFlag(RegionFlags::IS_IN_PROMOTE_SET); region->SetSpace(this); AddRegionToFirst(region); return true; } bool OldSpace::ContainObject(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { return true; } region = region->GetNext(); } return false; } bool OldSpace::IsLive(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { if (region->InCollectSet()) { return false; } auto freeObject = FreeObject::Cast(ToUintPtr(object)); return !freeObject->IsFreeObject(); } region = region->GetNext(); } return false; } void OldSpace::IterateOverObjects(const std::function &visitor) const { EnumerateRegions([&](Region *region) { if (region->InCollectSet()) { return; } uintptr_t curPtr = region->GetBegin(); uintptr_t endPtr = region->GetEnd(); while (curPtr < endPtr) { auto freeObject = FreeObject::Cast(curPtr); size_t objSize; if (!freeObject->IsFreeObject()) { auto obj = reinterpret_cast(curPtr); visitor(obj); objSize = obj->GetClass()->SizeFromJSHClass(obj); } else { objSize = freeObject->Available(); } curPtr += objSize; CHECK_OBJECT_SIZE(objSize); } CHECK_REGION_END(curPtr, endPtr); }); } size_t OldSpace::GetHeapObjectSize() const { size_t result; size_t availableSize = heap_->GetHeapManager()->GetOldSpaceAllocator().GetAvailableSize(); result = committedSize_ - availableSize; return result; } void OldSpace::Merge(Space *fromSpace) { ASSERT(GetSpaceType() == fromSpace->GetSpaceType()); fromSpace->EnumerateRegions([&](Region *region) { fromSpace->DecrementCommitted(region->GetCapacity()); AddRegion(region); }); fromSpace->GetRegionList().Clear(); } void OldSpace::AddRegionToCSet(Region *region) { region->SetFlag(RegionFlags::IS_IN_COLLECT_SET); collectRegionSet_.emplace_back(region); } void OldSpace::ClearRegionFromCSet() { EnumerateCollectRegionSet([](Region *region) { region->ClearFlag(RegionFlags::IS_IN_COLLECT_SET); }); collectRegionSet_.clear(); } void OldSpace::RemoveRegionFromCSetAndList(Region *region) { for (auto current = collectRegionSet_.begin(); current != collectRegionSet_.end(); current++) { if (*current == region) { region->ClearFlag(RegionFlags::IS_IN_COLLECT_SET); current = collectRegionSet_.erase(current); break; } } RemoveRegion(region); } void OldSpace::RemoveCSetFromList() { EnumerateCollectRegionSet([this](Region *current) { GetRegionList().RemoveNode(current); }); } void OldSpace::ReclaimRegionCSet() { EnumerateCollectRegionSet([this](Region *current) { ClearAndFreeRegion(current); }); collectRegionSet_.clear(); } void OldSpace::SelectCSet() { EnumerateRegions([this](Region *region) { if (!region->MostObjectAlive()) { collectRegionSet_.emplace_back(region); } }); if (collectRegionSet_.size() < PARTIAL_GC_MIN_COLLECT_REGION_SIZE) { heap_->SetOnlyMarkSemi(true); collectRegionSet_.clear(); return; } // sort std::sort(collectRegionSet_.begin(), collectRegionSet_.end(), [](Region *first, Region *second) { return first->AliveObject() < second->AliveObject(); }); unsigned long selectedRegionNumber = GetSelectedRegionNumber(); if (collectRegionSet_.size() > selectedRegionNumber) { collectRegionSet_.resize(selectedRegionNumber); } for (Region *region : collectRegionSet_) { region->SetFlag(RegionFlags::IS_IN_COLLECT_SET); } } NonMovableSpace::NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::NON_MOVABLE, initialCapacity, maximumCapacity) { } bool NonMovableSpace::Expand() { if (committedSize_ >= maximumCapacity_) { LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of non movable space is too big. "; return false; } Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); region->SetFlag(IS_IN_NON_MOVABLE_GENERATION); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } region->InitializeKind(); AddRegion(region); return true; } size_t NonMovableSpace::GetHeapObjectSize() const { size_t result; size_t availableSize = heap_->GetHeapManager()->GetNonMovableSpaceAllocator().GetAvailableSize(); result = GetCommittedSize() - availableSize; return result; } SnapShotSpace::SnapShotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity) { } bool SnapShotSpace::Expand(uintptr_t top) { if (committedSize_ >= maximumCapacity_) { return false; } Region *current = GetCurrentRegion(); if (current != nullptr) { current->SetHighWaterMark(top); } Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_SNAPSHOT_SPACE_SIZE); region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } AddRegion(region); return true; } void Space::Destroy() { ReclaimRegions(); } void NonMovableSpace::IterateOverObjects(const std::function &visitor) const { EnumerateRegions([&](Region *region) { uintptr_t curPtr = region->GetBegin(); uintptr_t endPtr = region->GetEnd(); while (curPtr < endPtr) { auto freeObject = FreeObject::Cast(curPtr); size_t objSize; if (!freeObject->IsFreeObject()) { auto obj = reinterpret_cast(curPtr); visitor(obj); objSize = obj->GetClass()->SizeFromJSHClass(obj); } else { objSize = freeObject->Available(); } curPtr += objSize; CHECK_OBJECT_SIZE(objSize); } CHECK_REGION_END(curPtr, endPtr); }); } bool NonMovableSpace::ContainObject(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { return true; } region = region->GetNext(); } return false; } bool NonMovableSpace::IsLive(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { auto freeObject = FreeObject::Cast(ToUintPtr(object)); return !freeObject->IsFreeObject(); } region = region->GetNext(); } return false; } HugeObjectSpace::HugeObjectSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::HUGE_OBJECT_SPACE, initialCapacity, maximumCapacity) { } uintptr_t HugeObjectSpace::Allocate(size_t objectSize) { if (committedSize_ >= maximumCapacity_) { LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of huge object space is too big. " << " old space committed: " << heap_->GetOldSpace()->GetCommittedSize() << " old space limit: " << heap_->GetOldSpaceAllocLimit() << " length: " << GetRegionList().GetLength(); return 0; } size_t alignedSize = AlignUp(objectSize + sizeof(Region), PANDA_POOL_ALIGNMENT_IN_BYTES); if (UNLIKELY(alignedSize > MAX_HUGE_OBJECT_SIZE)) { LOG_ECMA_MEM(FATAL) << "The size is too big for this allocator. Return nullptr."; return 0; } Region *region = regionFactory_->AllocateAlignedRegion(this, alignedSize); region->SetFlag(RegionFlags::IS_HUGE_OBJECT); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } AddRegion(region); return region->GetBegin(); } void HugeObjectSpace::Free(Region *region) { GetRegionList().RemoveNode(region); ClearAndFreeRegion(region); } bool HugeObjectSpace::ContainObject(TaggedObject *object) const { auto region = GetRegionList().GetFirst(); while (region != nullptr) { if (region->InRange(ToUintPtr(object))) { return true; } region = region->GetNext(); } return false; } bool HugeObjectSpace::IsLive(TaggedObject *object) const { return ContainObject(object); } size_t HugeObjectSpace::GetHeapObjectSize() const { return committedSize_; } void HugeObjectSpace::IterateOverObjects(const std::function &objectVisitor) const { EnumerateRegions([&](Region *region) { uintptr_t curPtr = region->GetBegin(); objectVisitor(reinterpret_cast(curPtr)); }); } MachineCodeSpace::MachineCodeSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::MACHINE_CODE_SPACE, initialCapacity, maximumCapacity) { } bool MachineCodeSpace::Expand() { if (committedSize_ >= maximumCapacity_) { LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of machine Code space is too big. "; return false; } Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); region->SetFlag(IS_IN_NON_MOVABLE_GENERATION); region->InitializeKind(); AddRegion(region); if (!thread_->IsReadyToMark()) { region->SetMarking(true); } int res = region->SetCodeExecutableAndReadable(); LOG_ECMA_MEM(DEBUG) << "MachineCodeSpace::Expand() SetCodeExecutableAndReadable" << res; return true; } size_t MachineCodeSpace::GetHeapObjectSize() const { size_t result = 0; size_t availableSize = GetHeap()->GetHeapManager()->GetMachineCodeSpaceAllocator().GetAvailableSize(); result = committedSize_ - availableSize; return result; } } // namespace panda::ecmascript