Files
wengchangcheng cd90e396a7 Descriptor: ark hilog refactor
details:
1. use hilog in ohos device
2. use panda logger in host
issue:  https://gitee.com/openharmony/ark_js_runtime/issues/I5FR5J

Signed-off-by: wengchangcheng <wengchangcheng@huawei.com>
Change-Id: I6f5de00751154bdb6aac3101515961a3a4432e80
2022-07-06 22:55:49 +08:00

803 lines
32 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2022 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/free_object.h"
#include "ecmascript/mem/heap-inl.h"
#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
#include <sys/sysinfo.h>
#endif
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
#include "ecmascript/ecma_vm.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/assert_scope.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/concurrent_sweeper.h"
#include "ecmascript/mem/full_gc.h"
#include "ecmascript/mem/mark_stack.h"
#include "ecmascript/mem/mem_controller.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/mem/native_area_allocator.h"
#include "ecmascript/mem/parallel_evacuator.h"
#include "ecmascript/mem/parallel_marker-inl.h"
#include "ecmascript/mem/stw_young_gc.h"
#include "ecmascript/mem/verification.h"
#include "ecmascript/mem/work_manager.h"
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/runtime_call_id.h"
#include "ecmascript/js_finalization_registry.h"
namespace panda::ecmascript {
Heap::Heap(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm), thread_(ecmaVm->GetJSThread()),
nativeAreaAllocator_(ecmaVm->GetNativeAreaAllocator()),
heapRegionAllocator_(ecmaVm->GetHeapRegionAllocator()) {}
void Heap::Initialize()
{
memController_ = new MemController(this);
auto &config = ecmaVm_->GetEcmaParamConfiguration();
size_t maxHeapSize = config.GetMaxHeapSize();
size_t minSemiSpaceCapacity = config.GetMinSemiSpaceSize();
size_t maxSemiSpaceCapacity = config.GetMaxSemiSpaceSize();
activeSemiSpace_ = new SemiSpace(this, minSemiSpaceCapacity, maxSemiSpaceCapacity);
activeSemiSpace_->Restart();
activeSemiSpace_->SetWaterLine();
auto topAddress = activeSemiSpace_->GetAllocationTopAddress();
auto endAddress = activeSemiSpace_->GetAllocationEndAddress();
thread_->ReSetNewSpaceAllocationAddress(topAddress, endAddress);
inactiveSemiSpace_ = new SemiSpace(this, minSemiSpaceCapacity, maxSemiSpaceCapacity);
// not set up from space
size_t readOnlySpaceCpacity = config.GetDefaultReadOnlySpaceSize();
readOnlySpace_ = new ReadOnlySpace(this, readOnlySpaceCpacity, readOnlySpaceCpacity);
size_t nonmovableSpaceCapacity = config.GetDefaultNonMovableSpaceSize();
if (ecmaVm_->GetJSOptions().WasSetMaxNonmovableSpaceCapacity()) {
nonmovableSpaceCapacity = ecmaVm_->GetJSOptions().MaxNonmovableSpaceCapacity();
}
nonMovableSpace_ = new NonMovableSpace(this, nonmovableSpaceCapacity, nonmovableSpaceCapacity);
nonMovableSpace_->Initialize();
size_t snapshotSpaceCapacity = config.GetDefaultSnapshotSpaceSize();
snapshotSpace_ = new SnapshotSpace(this, snapshotSpaceCapacity, snapshotSpaceCapacity);
size_t machineCodeSpaceCapacity = config.GetDefaultMachineCodeSpaceSize();
machineCodeSpace_ = new MachineCodeSpace(this, machineCodeSpaceCapacity, machineCodeSpaceCapacity);
machineCodeSpace_->Initialize();
size_t capacities = minSemiSpaceCapacity * 2 + nonmovableSpaceCapacity + snapshotSpaceCapacity +
machineCodeSpaceCapacity;
if (maxHeapSize < capacities || maxHeapSize - capacities < MIN_OLD_SPACE_LIMIT) {
LOG_ECMA_MEM(FATAL) << "HeapSize is too small to initialize oldspace, heapSize = " << maxHeapSize;
}
size_t oldSpaceCapacity = maxHeapSize - capacities;
globalSpaceAllocLimit_ = maxHeapSize - minSemiSpaceCapacity;
oldSpace_ = new OldSpace(this, oldSpaceCapacity, oldSpaceCapacity);
compressSpace_ = new OldSpace(this, oldSpaceCapacity, oldSpaceCapacity);
oldSpace_->Initialize();
size_t hugeObjectSpaceCapacity = config.GetDefaultHugeObjectSpaceSize();
hugeObjectSpace_ = new HugeObjectSpace(heapRegionAllocator_, hugeObjectSpaceCapacity, hugeObjectSpaceCapacity);
maxEvacuateTaskCount_ = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
maxMarkTaskCount_ = std::min<size_t>(ecmaVm_->GetJSOptions().GetGcThreadNum(),
maxEvacuateTaskCount_ - 1);
LOG_GC(INFO) << "heap initialize: heap size = " << maxHeapSize
<< ", semispace capacity = " << minSemiSpaceCapacity
<< ", nonmovablespace capacity = " << nonmovableSpaceCapacity
<< ", snapshotspace capacity = " << snapshotSpaceCapacity
<< ", machinecodespace capacity = " << machineCodeSpaceCapacity
<< ", oldspace capacity = " << oldSpaceCapacity
<< ", globallimit = " << globalSpaceAllocLimit_
<< ", gcThreadNum = " << maxMarkTaskCount_;
parallelGC_ = ecmaVm_->GetJSOptions().EnableParallelGC();
bool concurrentMarkerEnabled = ecmaVm_->GetJSOptions().EnableConcurrentMark();
markType_ = MarkType::MARK_YOUNG;
#if ECMASCRIPT_DISABLE_PARALLEL_GC
parallelGC_ = false;
#endif
#if ECMASCRIPT_DISABLE_CONCURRENT_MARKING
concurrentMarkerEnabled = false;
#endif
workManager_ = new WorkManager(this, Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() + 1);
stwYoungGC_ = new STWYoungGC(this, parallelGC_);
fullGC_ = new FullGC(this);
derivedPointers_ = new ChunkMap<DerivedDataKey, uintptr_t>(ecmaVm_->GetChunk());
partialGC_ = new PartialGC(this);
sweeper_ = new ConcurrentSweeper(this, ecmaVm_->GetJSOptions().EnableConcurrentSweep() ?
EnableConcurrentSweepType::ENABLE : EnableConcurrentSweepType::CONFIG_DISABLE);
concurrentMarker_ = new ConcurrentMarker(this, concurrentMarkerEnabled ? EnableConcurrentMarkType::ENABLE :
EnableConcurrentMarkType::CONFIG_DISABLE);
nonMovableMarker_ = new NonMovableMarker(this);
semiGCMarker_ = new SemiGCMarker(this);
compressGCMarker_ = new CompressGCMarker(this);
evacuator_ = new ParallelEvacuator(this);
}
void Heap::Destroy()
{
Prepare();
if (workManager_ != nullptr) {
delete workManager_;
workManager_ = nullptr;
}
if (activeSemiSpace_ != nullptr) {
activeSemiSpace_->Destroy();
delete activeSemiSpace_;
activeSemiSpace_ = nullptr;
}
if (inactiveSemiSpace_ != nullptr) {
inactiveSemiSpace_->Destroy();
delete inactiveSemiSpace_;
inactiveSemiSpace_ = nullptr;
}
if (oldSpace_ != nullptr) {
oldSpace_->Destroy();
delete oldSpace_;
oldSpace_ = nullptr;
}
if (compressSpace_ != nullptr) {
compressSpace_->Destroy();
delete compressSpace_;
compressSpace_ = nullptr;
}
if (nonMovableSpace_ != nullptr) {
nonMovableSpace_->Destroy();
delete nonMovableSpace_;
nonMovableSpace_ = nullptr;
}
if (snapshotSpace_ != nullptr) {
snapshotSpace_->Destroy();
delete snapshotSpace_;
snapshotSpace_ = nullptr;
}
if (machineCodeSpace_ != nullptr) {
machineCodeSpace_->Destroy();
delete machineCodeSpace_;
machineCodeSpace_ = nullptr;
}
if (hugeObjectSpace_ != nullptr) {
hugeObjectSpace_->Destroy();
delete hugeObjectSpace_;
hugeObjectSpace_ = nullptr;
}
if (readOnlySpace_ != nullptr) {
readOnlySpace_->ClearReadOnly();
readOnlySpace_->Destroy();
delete readOnlySpace_;
readOnlySpace_ = nullptr;
}
if (stwYoungGC_ != nullptr) {
delete stwYoungGC_;
stwYoungGC_ = nullptr;
}
if (partialGC_ != nullptr) {
delete partialGC_;
partialGC_ = nullptr;
}
if (fullGC_ != nullptr) {
delete fullGC_;
fullGC_ = nullptr;
}
nativeAreaAllocator_ = nullptr;
heapRegionAllocator_ = nullptr;
if (memController_ != nullptr) {
delete memController_;
memController_ = nullptr;
}
if (sweeper_ != nullptr) {
delete sweeper_;
sweeper_ = nullptr;
}
if (derivedPointers_ != nullptr) {
delete derivedPointers_;
derivedPointers_ = nullptr;
}
if (concurrentMarker_ != nullptr) {
delete concurrentMarker_;
concurrentMarker_ = nullptr;
}
if (nonMovableMarker_ != nullptr) {
delete nonMovableMarker_;
nonMovableMarker_ = nullptr;
}
if (semiGCMarker_ != nullptr) {
delete semiGCMarker_;
semiGCMarker_ = nullptr;
}
if (compressGCMarker_ != nullptr) {
delete compressGCMarker_;
compressGCMarker_ = nullptr;
}
if (evacuator_ != nullptr) {
delete evacuator_;
evacuator_ = nullptr;
}
}
void Heap::Prepare()
{
MEM_ALLOCATE_AND_GC_TRACE(GetEcmaVM(), HeapPrepare);
WaitRunningTaskFinished();
sweeper_->EnsureAllTaskFinished();
WaitClearTaskFinished();
}
void Heap::Resume(TriggerGCType gcType)
{
if (gcType == TriggerGCType::FULL_GC) {
compressSpace_->SetInitialCapacity(oldSpace_->GetInitialCapacity());
auto *oldSpace = compressSpace_;
compressSpace_ = oldSpace_;
oldSpace_ = oldSpace;
}
if (activeSemiSpace_->AdjustCapacity(inactiveSemiSpace_->GetAllocatedSizeSinceGC())) {
// if activeSpace capacity changes oldSpace maximumCapacity should change, too.
size_t multiple = 2;
size_t oldSpaceMaxLimit = 0;
if (activeSemiSpace_->GetInitialCapacity() >= inactiveSemiSpace_->GetInitialCapacity()) {
size_t delta = activeSemiSpace_->GetInitialCapacity() - inactiveSemiSpace_->GetInitialCapacity();
oldSpaceMaxLimit = oldSpace_->GetMaximumCapacity() - delta * multiple;
} else {
size_t delta = inactiveSemiSpace_->GetInitialCapacity() - activeSemiSpace_->GetInitialCapacity();
oldSpaceMaxLimit = oldSpace_->GetMaximumCapacity() + delta * multiple;
}
oldSpace_->SetMaximumCapacity(oldSpaceMaxLimit);
inactiveSemiSpace_->SetInitialCapacity(activeSemiSpace_->GetInitialCapacity());
}
activeSemiSpace_->SetWaterLine();
PrepareRecordRegionsForReclaim();
hugeObjectSpace_->RecliamHugeRegion();
if (parallelGC_) {
clearTaskFinished_ = false;
Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<AsyncClearTask>(this, gcType));
} else {
ReclaimRegions(gcType);
}
}
void Heap::CompactHeapBeforeFork()
{
fullGC_->RunPhasesForAppSpawn();
}
TriggerGCType Heap::SelectGCType() const
{
// If concurrent mark is enabled, the TryTriggerConcurrentMarking decide which GC to choose.
if (concurrentMarker_->IsEnabled()) {
return YOUNG_GC;
}
if (oldSpace_->CanExpand(activeSemiSpace_->GetSurvivalObjectSize()) &&
GetHeapObjectSize() <= globalSpaceAllocLimit_) {
return YOUNG_GC;
}
return OLD_GC;
}
void Heap::CollectGarbage(TriggerGCType gcType)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
[[maybe_unused]] GcStateScope scope(thread_);
#endif
CHECK_NO_GC
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
isVerifying_ = true;
// pre gc heap verify
sweeper_->EnsureAllTaskFinished();
auto failCount = Verification(this).VerifyAll();
if (failCount > 0) {
LOG_GC(FATAL) << "Before gc heap corrupted and " << failCount << " corruptions";
}
isVerifying_ = false;
#endif
#if ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC
gcType = TriggerGCType::FULL_GC;
#endif
if (fullGCRequested_ && thread_->IsReadyToMark() && gcType != TriggerGCType::FULL_GC) {
gcType = TriggerGCType::FULL_GC;
}
size_t originalNewSpaceSize = activeSemiSpace_->GetHeapObjectSize();
memController_->StartCalculationBeforeGC();
LOG_GC(INFO) << "Heap::CollectGarbage, gcType = " << gcType;
OPTIONAL_LOG(ecmaVm_, ERROR) << " global CommittedSize " << GetCommittedSize()
<< " global limit " << globalSpaceAllocLimit_;
switch (gcType) {
case TriggerGCType::YOUNG_GC:
// Use partial GC for young generation.
if (!concurrentMarker_->IsEnabled()) {
SetMarkType(MarkType::MARK_YOUNG);
}
partialGC_->RunPhases();
break;
case TriggerGCType::OLD_GC:
if (concurrentMarker_->IsEnabled() && markType_ == MarkType::MARK_YOUNG) {
// Wait for existing concurrent marking tasks to be finished (if any),
// and reset concurrent marker's status for full mark.
bool concurrentMark = CheckOngoingConcurrentMarking();
if (concurrentMark) {
concurrentMarker_->Reset();
}
}
SetMarkType(MarkType::MARK_FULL);
partialGC_->RunPhases();
break;
case TriggerGCType::FULL_GC:
fullGC_->RunPhases();
if (fullGCRequested_) {
fullGCRequested_ = false;
}
break;
default:
UNREACHABLE();
break;
}
if (!oldSpaceLimitAdjusted_ && originalNewSpaceSize > 0) {
semiSpaceCopiedSize_ = activeSemiSpace_->GetHeapObjectSize();
double copiedRate = semiSpaceCopiedSize_ * 1.0 / originalNewSpaceSize;
promotedSize_ = GetEvacuator()->GetPromotedSize();
double promotedRate = promotedSize_ * 1.0 / originalNewSpaceSize;
memController_->AddSurvivalRate(std::min(copiedRate + promotedRate, 1.0));
AdjustOldSpaceLimit();
}
memController_->StopCalculationAfterGC(gcType);
if (gcType == TriggerGCType::FULL_GC || IsFullMark()) {
// Only when the gc type is not semiGC and after the old space sweeping has been finished,
// the limits of old space and global space can be recomputed.
RecomputeLimits();
OPTIONAL_LOG(ecmaVm_, ERROR) << " GC after: is full mark" << IsFullMark()
<< " global CommittedSize " << GetCommittedSize()
<< " global limit " << globalSpaceAllocLimit_;
markType_ = MarkType::MARK_YOUNG;
}
if (concurrentMarker_->IsRequestDisabled()) {
concurrentMarker_->EnableConcurrentMarking(EnableConcurrentMarkType::DISABLE);
}
ecmaVm_->GetEcmaGCStats()->CheckIfLongTimePause();
# if ECMASCRIPT_ENABLE_GC_LOG
ecmaVm_->GetEcmaGCStats()->PrintStatisticResult();
#endif
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
// post gc heap verify
isVerifying_ = true;
sweeper_->EnsureAllTaskFinished();
failCount = Verification(this).VerifyAll();
if (failCount > 0) {
LOG_GC(FATAL) << "After gc heap corrupted and " << failCount << " corruptions";
}
isVerifying_ = false;
#endif
JSFinalizationRegistry::CheckAndCall(thread_);
}
void Heap::ThrowOutOfMemoryError(size_t size, std::string functionName)
{
GetEcmaVM()->GetEcmaGCStats()->PrintHeapStatisticResult(true);
LOG_ECMA_MEM(FATAL) << "OOM when trying to allocate " << size << " bytes"
<< " function name: " << functionName.c_str();
}
size_t Heap::VerifyHeapObjects() const
{
size_t failCount = 0;
{
VerifyObjectVisitor verifier(this, &failCount);
activeSemiSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
oldSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
nonMovableSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
hugeObjectSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
machineCodeSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
snapshotSpace_->IterateOverObjects(verifier);
}
return failCount;
}
void Heap::AdjustOldSpaceLimit()
{
if (oldSpaceLimitAdjusted_) {
return;
}
size_t minGrowingStep = ecmaVm_->GetEcmaParamConfiguration().GetMinGrowingStep();
size_t oldSpaceAllocLimit = GetOldSpace()->GetInitialCapacity();
size_t newOldSpaceAllocLimit = std::max(oldSpace_->GetHeapObjectSize() + minGrowingStep,
static_cast<size_t>(oldSpaceAllocLimit * memController_->GetAverageSurvivalRate()));
if (newOldSpaceAllocLimit <= oldSpaceAllocLimit) {
GetOldSpace()->SetInitialCapacity(newOldSpaceAllocLimit);
} else {
oldSpaceLimitAdjusted_ = true;
}
size_t newGlobalSpaceAllocLimit = std::max(GetHeapObjectSize() + minGrowingStep,
static_cast<size_t>(globalSpaceAllocLimit_ * memController_->GetAverageSurvivalRate()));
if (newGlobalSpaceAllocLimit < globalSpaceAllocLimit_) {
globalSpaceAllocLimit_ = newGlobalSpaceAllocLimit;
}
OPTIONAL_LOG(ecmaVm_, ERROR) << "AdjustOldSpaceLimit oldSpaceAllocLimit_" << oldSpaceAllocLimit
<< " globalSpaceAllocLimit_" << globalSpaceAllocLimit_;
}
void Heap::AddToKeptObjects(JSHandle<JSTaggedValue> value) const
{
JSHandle<GlobalEnv> env = ecmaVm_->GetGlobalEnv();
JSHandle<LinkedHashSet> linkedSet;
if (env->GetWeakRefKeepObjects()->IsUndefined()) {
linkedSet = LinkedHashSet::Create(thread_);
} else {
linkedSet =
JSHandle<LinkedHashSet>(thread_, LinkedHashSet::Cast(env->GetWeakRefKeepObjects()->GetTaggedObject()));
}
linkedSet = LinkedHashSet::Add(thread_, linkedSet, value);
env->SetWeakRefKeepObjects(thread_, linkedSet);
}
void Heap::ClearKeptObjects() const
{
ecmaVm_->GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined());
}
void Heap::RecomputeLimits()
{
double gcSpeed = memController_->CalculateMarkCompactSpeedPerMS();
double mutatorSpeed = memController_->GetCurrentOldSpaceAllocationThroughputPerMS();
size_t oldSpaceSize = oldSpace_->GetHeapObjectSize() + hugeObjectSpace_->GetHeapObjectSize();
size_t newSpaceCapacity = activeSemiSpace_->GetInitialCapacity();
double growingFactor = memController_->CalculateGrowingFactor(gcSpeed, mutatorSpeed);
// newOldSpaceLimit should consider committedSize of hugeObjectSpace
size_t maxOldSpaceCapacity = oldSpace_->GetMaximumCapacity() - hugeObjectSpace_->GetCommittedSize();
auto newOldSpaceLimit = memController_->CalculateAllocLimit(oldSpaceSize, MIN_OLD_SPACE_LIMIT, maxOldSpaceCapacity,
newSpaceCapacity, growingFactor);
size_t maxGlobalSize = ecmaVm_->GetEcmaParamConfiguration().GetMaxHeapSize() - newSpaceCapacity;
auto newGlobalSpaceLimit = memController_->CalculateAllocLimit(GetHeapObjectSize(), MIN_HEAP_SIZE,
maxGlobalSize, newSpaceCapacity, growingFactor);
globalSpaceAllocLimit_ = newGlobalSpaceLimit;
oldSpace_->SetInitialCapacity(newOldSpaceLimit);
OPTIONAL_LOG(ecmaVm_, ERROR) << "RecomputeLimits oldSpaceAllocLimit_" << newOldSpaceLimit
<< " globalSpaceAllocLimit_" << globalSpaceAllocLimit_;
}
void Heap::CheckAndTriggerOldGC()
{
if (GetHeapObjectSize() > globalSpaceAllocLimit_) {
CollectGarbage(TriggerGCType::OLD_GC);
}
}
bool Heap::CheckOngoingConcurrentMarking()
{
if (concurrentMarker_->IsEnabled() && !thread_->IsReadyToMark()) {
if (thread_->IsMarking()) {
[[maybe_unused]] ClockScope clockScope;
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Heap::CheckOngoingConcurrentMarking");
MEM_ALLOCATE_AND_GC_TRACE(GetEcmaVM(), WaitConcurrentMarkingFinished);
GetNonMovableMarker()->ProcessMarkStack(MAIN_THREAD_INDEX);
WaitConcurrentMarkingFinished();
ecmaVm_->GetEcmaGCStats()->StatisticConcurrentMarkWait(clockScope.GetPauseTime());
LOG_GC(DEBUG) << "wait concurrent marking finish pause time " << clockScope.TotalSpentTime();
}
memController_->RecordAfterConcurrentMark(IsFullMark(), concurrentMarker_);
return true;
}
return false;
}
void Heap::TryTriggerConcurrentMarking()
{
// When concurrent marking is enabled, concurrent marking will be attempted to trigger.
// When the size of old space or global space reaches the limit, isFullMarkNeeded will be set to true.
// If the predicted duration of current full mark may not result in the new and old spaces reaching their limit,
// full mark will be triggered.
// In the same way, if the size of the new space reaches the capacity, and the predicted duration of current
// young mark may not result in the new space reaching its limit, young mark can be triggered.
// If it spends much time in full mark, the compress full GC will be requested when the spaces reach the limit.
// If the global space is larger than half max heap size, we will turn to use full mark and trigger partial GC.
if (!concurrentMarker_->IsEnabled() || !thread_->IsReadyToMark()) {
return;
}
bool isFullMarkNeeded = false;
double oldSpaceMarkDuration = 0, newSpaceMarkDuration = 0, newSpaceRemainSize = 0, newSpaceAllocToLimitDuration = 0,
oldSpaceAllocToLimitDuration = 0;
double oldSpaceAllocSpeed = memController_->GetOldSpaceAllocationThroughputPerMS();
double oldSpaceConcurrentMarkSpeed = memController_->GetFullSpaceConcurrentMarkSpeedPerMS();
size_t oldSpaceHeapObjectSize = oldSpace_->GetHeapObjectSize() + hugeObjectSpace_->GetHeapObjectSize();
size_t globalHeapObjectSize = GetHeapObjectSize();
size_t oldSpaceAllocLimit = oldSpace_->GetInitialCapacity();
if (oldSpaceConcurrentMarkSpeed == 0 || oldSpaceAllocSpeed == 0) {
if (oldSpaceHeapObjectSize >= oldSpaceAllocLimit || globalHeapObjectSize >= globalSpaceAllocLimit_) {
markType_ = MarkType::MARK_FULL;
OPTIONAL_LOG(ecmaVm_, ERROR) << "Trigger the first full mark";
TriggerConcurrentMarking();
return;
}
} else {
if (oldSpaceHeapObjectSize >= oldSpaceAllocLimit || globalHeapObjectSize >= globalSpaceAllocLimit_) {
isFullMarkNeeded = true;
}
oldSpaceAllocToLimitDuration = (oldSpaceAllocLimit - oldSpaceHeapObjectSize) / oldSpaceAllocSpeed;
oldSpaceMarkDuration = GetHeapObjectSize() / oldSpaceConcurrentMarkSpeed;
// oldSpaceRemainSize means the predicted size which can be allocated after the full concurrent mark.
double oldSpaceRemainSize = (oldSpaceAllocToLimitDuration - oldSpaceMarkDuration) * oldSpaceAllocSpeed;
if (oldSpaceRemainSize > 0 && oldSpaceRemainSize < DEFAULT_REGION_SIZE) {
isFullMarkNeeded = true;
}
}
double newSpaceAllocSpeed = memController_->GetNewSpaceAllocationThroughputPerMS();
double newSpaceConcurrentMarkSpeed = memController_->GetNewSpaceConcurrentMarkSpeedPerMS();
if (newSpaceConcurrentMarkSpeed == 0 || newSpaceAllocSpeed == 0) {
auto &config = ecmaVm_->GetEcmaParamConfiguration();
if (activeSemiSpace_->GetCommittedSize() >= config.GetSemiSpaceTriggerConcurrentMark()) {
markType_ = MarkType::MARK_YOUNG;
TriggerConcurrentMarking();
OPTIONAL_LOG(ecmaVm_, ERROR) << "Trigger the first semi mark" << fullGCRequested_;
}
return;
}
newSpaceAllocToLimitDuration = (activeSemiSpace_->GetInitialCapacity() - activeSemiSpace_->GetCommittedSize())
/ newSpaceAllocSpeed;
newSpaceMarkDuration = activeSemiSpace_->GetHeapObjectSize() / newSpaceConcurrentMarkSpeed;
// newSpaceRemainSize means the predicted size which can be allocated after the semi concurrent mark.
newSpaceRemainSize = (newSpaceAllocToLimitDuration - newSpaceMarkDuration) * newSpaceAllocSpeed;
if (isFullMarkNeeded) {
if (oldSpaceMarkDuration < newSpaceAllocToLimitDuration
&& oldSpaceMarkDuration < oldSpaceAllocToLimitDuration) {
markType_ = MarkType::MARK_FULL;
TriggerConcurrentMarking();
OPTIONAL_LOG(ecmaVm_, ERROR) << "Trigger full mark by speed";
} else {
if (oldSpaceHeapObjectSize >= oldSpaceAllocLimit || globalHeapObjectSize >= globalSpaceAllocLimit_) {
markType_ = MarkType::MARK_FULL;
TriggerConcurrentMarking();
OPTIONAL_LOG(ecmaVm_, ERROR) << "Trigger full mark by limit";
}
}
} else if (newSpaceRemainSize < DEFAULT_REGION_SIZE) {
markType_ = MarkType::MARK_YOUNG;
TriggerConcurrentMarking();
OPTIONAL_LOG(ecmaVm_, ERROR) << "Trigger semi mark";
}
}
void Heap::TriggerConcurrentMarking()
{
if (concurrentMarker_->IsEnabled() && !fullGCRequested_) {
concurrentMarker_->Mark();
}
}
void Heap::UpdateDerivedObjectInStack()
{
if (derivedPointers_->empty()) {
return;
}
for (auto derived : *derivedPointers_) {
auto baseAddr = reinterpret_cast<JSTaggedValue *>(derived.first.first);
JSTaggedValue base = *baseAddr;
if (base.IsHeapObject()) {
uintptr_t baseOldObject = derived.second;
uintptr_t *derivedAddr = reinterpret_cast<uintptr_t *>(derived.first.second);
#ifndef NDEBUG
LOG_GC(DEBUG) << std::hex << "fix base before:" << baseAddr << " base old Value: " << baseOldObject <<
" derived:" << derivedAddr << " old Value: " << *derivedAddr << std::endl;
#endif
// derived is always bigger than base
*derivedAddr = reinterpret_cast<uintptr_t>(base.GetTaggedObject()) + (*derivedAddr - baseOldObject);
#ifndef NDEBUG
LOG_GC(DEBUG) << std::hex << "fix base after:" << baseAddr <<
" base New Value: " << base.GetTaggedObject() <<
" derived:" << derivedAddr << " New Value: " << *derivedAddr << std::endl;
#endif
}
}
derivedPointers_->clear();
}
void Heap::WaitRunningTaskFinished()
{
os::memory::LockHolder holder(waitTaskFinishedMutex_);
while (runningTaskCount_ > 0) {
waitTaskFinishedCV_.Wait(&waitTaskFinishedMutex_);
}
}
void Heap::WaitClearTaskFinished()
{
os::memory::LockHolder holder(waitClearTaskFinishedMutex_);
while (!clearTaskFinished_) {
waitClearTaskFinishedCV_.Wait(&waitClearTaskFinishedMutex_);
}
}
void Heap::WaitConcurrentMarkingFinished()
{
concurrentMarker_->WaitMarkingFinished();
}
void Heap::PostParallelGCTask(ParallelGCTaskPhase gcTask)
{
IncreaseTaskCount();
Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<ParallelGCTask>(this, gcTask));
}
void Heap::IncreaseTaskCount()
{
os::memory::LockHolder holder(waitTaskFinishedMutex_);
runningTaskCount_++;
}
void Heap::ChangeGCParams(bool inBackground)
{
if (inBackground) {
LOG_GC(INFO) << "app is inBackground";
if (GetMemGrowingType() != MemGrowingType::PRESSURE) {
SetMemGrowingType(MemGrowingType::CONSERVATIVE);
LOG_GC(INFO) << "Heap Growing Type CONSERVATIVE";
}
concurrentMarker_->EnableConcurrentMarking(EnableConcurrentMarkType::DISABLE);
sweeper_->EnableConcurrentSweep(EnableConcurrentSweepType::DISABLE);
maxMarkTaskCount_ = 1;
maxEvacuateTaskCount_ = 1;
} else {
LOG_GC(INFO) << "app is not inBackground";
if (GetMemGrowingType() != MemGrowingType::PRESSURE) {
SetMemGrowingType(MemGrowingType::HIGH_THROUGHPUT);
LOG_GC(INFO) << "Heap Growing Type HIGH_THROUGHPUT";
}
concurrentMarker_->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
sweeper_->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
maxMarkTaskCount_ = std::min<size_t>(ecmaVm_->GetJSOptions().GetGcThreadNum(),
Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() - 1);
maxEvacuateTaskCount_ = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
}
}
void Heap::NotifyMemoryPressure(bool inHighMemoryPressure)
{
if (inHighMemoryPressure) {
LOG_GC(INFO) << "app is inHighMemoryPressure";
SetMemGrowingType(MemGrowingType::PRESSURE);
} else {
LOG_GC(INFO) << "app is not inHighMemoryPressure";
SetMemGrowingType(MemGrowingType::CONSERVATIVE);
}
}
bool Heap::CheckCanDistributeTask()
{
os::memory::LockHolder holder(waitTaskFinishedMutex_);
return runningTaskCount_ < maxMarkTaskCount_;
}
void Heap::ReduceTaskCount()
{
os::memory::LockHolder holder(waitTaskFinishedMutex_);
runningTaskCount_--;
if (runningTaskCount_ == 0) {
waitTaskFinishedCV_.SignalAll();
}
}
bool Heap::ParallelGCTask::Run(uint32_t threadIndex)
{
switch (taskPhase_) {
case ParallelGCTaskPhase::SEMI_HANDLE_THREAD_ROOTS_TASK:
heap_->GetSemiGCMarker()->MarkRoots(threadIndex);
heap_->GetSemiGCMarker()->ProcessMarkStack(threadIndex);
break;
case ParallelGCTaskPhase::SEMI_HANDLE_SNAPSHOT_TASK:
heap_->GetSemiGCMarker()->ProcessSnapshotRSet(threadIndex);
break;
case ParallelGCTaskPhase::SEMI_HANDLE_GLOBAL_POOL_TASK:
heap_->GetSemiGCMarker()->ProcessMarkStack(threadIndex);
break;
case ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK:
heap_->GetNonMovableMarker()->ProcessMarkStack(threadIndex);
break;
case ParallelGCTaskPhase::COMPRESS_HANDLE_GLOBAL_POOL_TASK:
heap_->GetCompressGCMarker()->ProcessMarkStack(threadIndex);
break;
case ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK:
heap_->GetNonMovableMarker()->ProcessMarkStack(threadIndex);
break;
case ParallelGCTaskPhase::CONCURRENT_HANDLE_OLD_TO_NEW_TASK:
heap_->GetNonMovableMarker()->ProcessOldToNew(threadIndex);
break;
default:
break;
}
heap_->ReduceTaskCount();
return true;
}
bool Heap::AsyncClearTask::Run([[maybe_unused]] uint32_t threadIndex)
{
heap_->ReclaimRegions(gcType_);
return true;
}
size_t Heap::GetArrayBufferSize() const
{
size_t result = 0;
this->IterateOverObjects([&result](TaggedObject *obj) {
JSHClass* jsClass = obj->GetClass();
result += jsClass->IsArrayBuffer() ? jsClass->GetObjectSize() : 0;
});
return result;
}
bool Heap::IsAlive(TaggedObject *object) const
{
if (!ContainObject(object)) {
LOG_GC(ERROR) << "The region is already free";
return false;
}
bool isFree = object->GetClass() != nullptr && FreeObject::Cast(ToUintPtr(object))->IsFreeObject();
if (isFree) {
Region *region = Region::ObjectAddressToRange(object);
LOG_GC(ERROR) << "The object " << object << " in "
<< region->GetSpaceTypeName()
<< " already free";
}
return !isFree;
}
bool Heap::ContainObject(TaggedObject *object) const
{
/*
* fixme: There's no absolutely safe appraoch to doing this, given that the region object is currently
* allocated and maintained in the JS object heap. We cannot safely tell whether a region object
* calculated from an object address is still valid or alive in a cheap way.
* This will introduce inaccurate result to verify if an object is contained in the heap, and it may
* introduce additional incorrect memory access issues.
* Unless we can tolerate the performance impact of iterating the region list of each space and change
* the implementation to that approach, don't rely on current implementation to get accurate result.
*/
Region *region = Region::ObjectAddressToRange(object);
return region->InHeapSpace();
}
} // namespace panda::ecmascript