* Copyright (c) 2025 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 "common_components/heap/allocator/regional_heap.h"
#include "common_components/heap/space/from_space.h"
#include "common_components/heap/space/old_space.h"
#include "common_components/heap/collector/collector_resources.h"
#include "common_components/taskpool/taskpool.h"
#if defined(COMMON_SANITIZER_SUPPORT)
#include "common_components/base/asan_interface.h"
#endif
namespace common {
void FromSpace::DumpRegionStats() const
{
size_t fromRegions = fromRegionList_.GetRegionCount();
size_t fromUnits = fromRegionList_.GetUnitCount();
size_t fromSize = fromUnits * RegionDesc::UNIT_SIZE;
size_t allocFromSize = fromRegionList_.GetAllocatedSize();
size_t exemptedFromRegions = exemptedFromRegionList_.GetRegionCount();
size_t exemptedFromUnits = exemptedFromRegionList_.GetUnitCount();
size_t exemptedFromSize = exemptedFromUnits * RegionDesc::UNIT_SIZE;
size_t allocExemptedFromSize = exemptedFromRegionList_.GetAllocatedSize();
size_t units = fromUnits + exemptedFromUnits;
VLOG(DEBUG, "\tfrom space units: %zu (%zu B)", units, units * RegionDesc::UNIT_SIZE);
VLOG(DEBUG, "\t from-regions %zu: %zu units (%zu B, alloc %zu)", fromRegions, fromUnits, fromSize, allocFromSize);
VLOG(DEBUG, "\t exempted from-regions %zu: %zu units (%zu B, alloc %zu)",
exemptedFromRegions, exemptedFromUnits, exemptedFromSize, allocExemptedFromSize);
}
void FromSpace::ExemptFromRegions()
{
size_t forwardBytes = 0;
size_t floatingGarbage = 0;
size_t oldFromBytes = fromRegionList_.GetUnitCount() * RegionDesc::UNIT_SIZE;
RegionDesc* fromRegion = fromRegionList_.GetHeadRegion();
while (fromRegion != nullptr) {
size_t threshold = static_cast<size_t>(exemptedRegionThreshold_ * fromRegion->GetRegionSize());
size_t liveBytes = fromRegion->GetLiveByteCount();
long rawPtrCnt = fromRegion->GetRawPointerObjectCount();
if (liveBytes > threshold) {
RegionDesc* del = fromRegion;
DLOG(REGION, "region %p @0x%zx+%zu exempted by forwarding: %zu units, %u live bytes", del,
del->GetRegionStart(), del->GetRegionAllocatedSize(),
del->GetUnitCount(), del->GetLiveByteCount());
fromRegion = fromRegion->GetNextRegion();
if (fromRegionList_.TryDeleteRegion(del, RegionDesc::RegionType::FROM_REGION,
RegionDesc::RegionType::EXEMPTED_FROM_REGION)) {
ExemptFromRegion(del);
}
floatingGarbage += (del->GetRegionSize() - del->GetLiveByteCount());
} else if (rawPtrCnt > 0) {
RegionDesc* del = fromRegion;
DLOG(REGION, "region %p @0x%zx+%zu pinned by forwarding: %zu units, %u live bytes rawPtr cnt %u",
del, del->GetRegionStart(), del->GetRegionAllocatedSize(),
del->GetUnitCount(), del->GetLiveByteCount(), rawPtrCnt);
fromRegion = fromRegion->GetNextRegion();
if (fromRegionList_.TryDeleteRegion(del, RegionDesc::RegionType::FROM_REGION,
RegionDesc::RegionType::RAW_POINTER_REGION)) {
heap_.AddRawPointerRegion(del);
}
floatingGarbage += (del->GetRegionSize() - del->GetLiveByteCount());
} else {
forwardBytes += fromRegion->GetLiveByteCount();
fromRegion = fromRegion->GetNextRegion();
}
}
size_t newFromBytes = fromRegionList_.GetUnitCount() * RegionDesc::UNIT_SIZE;
size_t exemptedFromBytes = exemptedFromRegionList_.GetUnitCount() * RegionDesc::UNIT_SIZE;
VLOG(DEBUG, "exempt from-space: %zu B - %zu B -> %zu B, %zu B floating garbage, %zu B to forward",
oldFromBytes, exemptedFromBytes, newFromBytes, floatingGarbage, forwardBytes);
}
class CopyTask : public Task {
public:
CopyTask(int32_t id, FromSpace& fromSpace, RegionDesc* region, size_t regionCnt, TaskPackMonitor &monitor)
: Task(id), fromSpace_(fromSpace), startRegion_(region), regionCount_(regionCnt), monitor_(monitor) {}
~CopyTask() override = default;
bool Run([[maybe_unused]] uint32_t threadIndex) override
{
ThreadLocal::SetThreadType(ThreadType::GC_THREAD);
fromSpace_.ParallelCopyFromRegions(startRegion_, regionCount_);
monitor_.NotifyFinishOne();
ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
return true;
}
private:
FromSpace &fromSpace_;
RegionDesc *startRegion_ {nullptr};
size_t regionCount_;
TaskPackMonitor &monitor_;
};
void FromSpace::ParallelCopyFromRegions(RegionDesc *startRegion, size_t regionCnt)
{
RegionDesc *currentRegion = startRegion;
for (size_t count = 0; (count < regionCnt) && currentRegion != nullptr; ++count) {
RegionDesc *region = currentRegion;
currentRegion = currentRegion->GetNextRegion();
heap_.CopyRegion(region);
}
AllocationBuffer* allocBuffer = AllocationBuffer::GetAllocBuffer();
if (LIKELY_CC(allocBuffer != nullptr)) {
allocBuffer->ClearRegions();
}
}
void FromSpace::CopyFromRegions()
{
RegionDesc* fromRegion = fromRegionList_.GetHeadRegion();
while (fromRegion != nullptr) {
ASSERT_LOGF(fromRegion->IsValidRegion(), "region is not head when get head region of from region list");
RegionDesc* region = fromRegion;
fromRegion = fromRegion->GetNextRegion();
heap_.CopyRegion(region);
}
VLOG(DEBUG, "forward %zu from-region units", fromRegionList_.GetUnitCount());
AllocationBuffer* allocBuffer = AllocationBuffer::GetAllocBuffer();
if (LIKELY(allocBuffer != nullptr)) {
allocBuffer->ClearRegions();
}
}
void FromSpace::CopyFromRegions(Taskpool* threadPool)
{
if (threadPool != nullptr) {
uint32_t parallel = Heap::GetHeap().GetCollectorResources().GetGCThreadCount(true) - 1;
uint32_t threadNum = parallel + 1;
size_t totalRegionCount = fromRegionList_.GetRegionCount();
if (UNLIKELY_CC(totalRegionCount == 0)) {
return;
}
size_t regionCntEachTask = totalRegionCount / static_cast<size_t>(threadNum);
size_t leftRegionCnt = totalRegionCount - regionCntEachTask * parallel;
RegionDesc* region = fromRegionList_.GetHeadRegion();
TaskPackMonitor monitor(parallel, parallel);
for (uint32_t i = 0; i < parallel; ++i) {
ASSERT_LOGF(region != nullptr, "from region list records wrong region info");
RegionDesc* startRegion = region;
for (size_t count = 0; count < regionCntEachTask; ++count) {
region = region->GetNextRegion();
}
threadPool->PostTask(std::make_unique<CopyTask>(0, *this, startRegion, regionCntEachTask, monitor));
}
ParallelCopyFromRegions(region, leftRegionCnt);
monitor.WaitAllFinished();
} else {
CopyFromRegions();
}
}
void FromSpace::GetPromotedTo(OldSpace& mspace)
{
mspace.PromoteRegionList(exemptedFromRegionList_);
}
}