* Copyright (c) 2024 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/shared_heap/shared_gc.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_marker.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#include "ecmascript/mem/shared_heap/shared_gc_evacuator.h"
#include "ecmascript/mem/shared_heap/shared_gc_marker-inl.h"
#include "ecmascript/mem/shared_heap/shared_gc_visitor-inl.h"
#include "ecmascript/mem/verification.h"
namespace panda::ecmascript {
void SharedGC::RunPhases()
{
ASSERT("SharedGC should be disabled" && !g_isEnableCMCGC);
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, ("SharedGC::RunPhases;GCReason"
+ std::to_string(static_cast<int>(sHeap_->GetEcmaGCStats()->GetGCReason()))
+ ";MarkReason" + std::to_string(static_cast<int>(sHeap_->GetEcmaGCStats()->GetMarkReason()))
+ ";Sensitive" + std::to_string(static_cast<int>(sHeap_->GetSensitiveStatus()))
+ ";IsInBackground" + std::to_string(Runtime::GetInstance()->IsInBackground())
+ ";Startup" + std::to_string(static_cast<int>(sHeap_->GetStartupStatus()))
+ ";Old" + std::to_string(sHeap_->GetOldSpace()->GetCommittedSize())
+ ";huge" + std::to_string(sHeap_->GetHugeObjectSpace()->GetCommittedSize())
+ ";NonMov" + std::to_string(sHeap_->GetNonMovableSpace()->GetCommittedSize())
+ ";TotCommit" + std::to_string(sHeap_->GetCommittedSize())
+ ";NativeBindingSize" + std::to_string(sHeap_->GetNativeSizeAfterLastGC())
+ ";NativeLimitGC" + std::to_string(sHeap_->GetNativeSizeTriggerSharedGC())
+ ";NativeLimitCM" + std::to_string(sHeap_->GetNativeSizeTriggerSharedCM())).c_str(), "");
TRACE_GC(GCStats::Scope::ScopeId::TotalGC, sHeap_->GetEcmaGCStats());
sHeap_->SetGCThreadQosPriority(common::PriorityMode::STW);
markingInProgress_ = sHeap_->CheckOngoingConcurrentMarking();
Initialize();
Mark();
if (UNLIKELY(sHeap_->ShouldVerifyHeap())) {
LOG_ECMA(DEBUG) << "start verify mark";
SharedHeapVerification(sHeap_, VerifyKind::VERIFY_SHARED_GC_MARK).VerifyMark(markingInProgress_);
}
PreSweep();
Evacuate();
Sweep();
if (UNLIKELY(sHeap_->ShouldVerifyHeap())) {
LOG_ECMA(DEBUG) << "start verify sweep";
SharedHeapVerification(sHeap_, VerifyKind::VERIFY_SHARED_GC_SWEEP).VerifySweep(markingInProgress_);
}
Finish();
if (!concurrentProcessStringTable_) {
sHeap_->SetGCThreadQosPriority(common::PriorityMode::FOREGROUND);
}
sHeap_->ResetNativeSizeAfterLastGC();
}
void SharedGC::Initialize()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SharedGC::Initialize", "");
TRACE_GC(GCStats::Scope::ScopeId::Initialize, sHeap_->GetEcmaGCStats());
if (!markingInProgress_) {
sHeap_->Prepare(true);
sHeap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
current->ClearMarkGCBitset();
current->ClearCrossRegionRSet();
});
sHeap_->EnumerateOldSpaceRegions([](Region *current) {
ASSERT(current->InSharedSweepableSpace());
current->ResetAliveObject();
});
sWorkManager_->Initialize(TriggerGCType::SHARED_GC, SharedParallelMarkPhase::SHARED_MARK_TASK, 0);
}
}
void SharedGC::MarkRoots(SharedMarkType markType)
{
SharedGCMarkRootVisitor sharedGCMarkRootVisitor(sWorkManager_->GetSharedGCWorkNodeHolder(DAEMON_THREAD_INDEX));
sHeap_->GetSharedGCMarker()->MarkRoots(sharedGCMarkRootVisitor, markType);
}
void SharedGC::Mark()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SharedGC::Mark", "");
TRACE_GC(GCStats::Scope::ScopeId::Mark, sHeap_->GetEcmaGCStats());
if (markingInProgress_) {
sHeap_->GetConcurrentMarker()->ReMark();
return;
}
SharedGCMarker *marker = sHeap_->GetSharedGCMarker();
MarkRoots(SharedMarkType::NOT_CONCURRENT_MARK);
SharedGCMarkLocalToShareRSetVisitor<SharedMarkType::NOT_CONCURRENT_MARK> rSetVisitor(
sWorkManager_->GetSharedGCWorkNodeHolder(DAEMON_THREAD_INDEX));
sHeap_->GetSharedGCMarker()->ProcessLocalToShareRSet(rSetVisitor);
sHeap_->GetSharedGCMarker()->ProcessMarkStack(DAEMON_THREAD_INDEX);
marker->MergeBackAndResetRSetWorkListHandler();
sHeap_->WaitRunningMarkTaskFinished();
}
void SharedGC::PreSweep()
{
if (markingInProgress_ && sHeap_->GetGCType() == TriggerGCType::SHARED_PARTIAL_GC) {
sHeap_->GetOldSpace()->SelectCSets();
}
sHeap_->GetSweeper()->Sweep(false);
UpdateRecordWeakReference();
}
void SharedGC::Evacuate()
{
if (sHeap_->HasCSetRegions()) {
sHeap_->GetSharedGCEvacuator()->Evacuate();
}
}
void SharedGC::Sweep()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SharedGC::Sweep", "");
TRACE_GC(GCStats::Scope::ScopeId::Sweep, sHeap_->GetEcmaGCStats());
WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) -> TaggedObject* {
Region *objectRegion = Region::ObjectAddressToRange(header);
if (UNLIKELY(objectRegion == nullptr)) {
return nullptr;
}
if (!objectRegion->InSharedSweepableSpace()) {
return header;
}
if (objectRegion->InSCollectSet()) {
MarkWord markWord(header, RELAXED_LOAD);
if (markWord.IsForwardingAddress()) {
return markWord.ToForwardingAddress();
}
return nullptr;
}
if (objectRegion->Test(header)) {
return header;
}
return nullptr;
};
auto stringTableCleaner = Runtime::GetInstance()->GetEcmaStringTable()->GetCleaner();
if (stringTableCleaner->IsEnableConcurrentSweep()) {
concurrentProcessStringTable_ = sHeap_->IsParallelGCEnabled() &&
!Runtime::GetInstance()->GetEcmaStringTable()->IsInUse();
} else {
concurrentProcessStringTable_ = false;
}
if (!concurrentProcessStringTable_) {
LOG_GC(DEBUG) << "process string table stw";
stringTableCleaner->PostSweepWeakRefTask(gcUpdateWeak);
}
bool needClearCache = sHeap_->HasCSetRegions();
Runtime::GetInstance()->ProcessSharedDelete(gcUpdateWeak);
Runtime::GetInstance()->GCIterateThreadList([&gcUpdateWeak, needClearCache](JSThread *thread) {
ASSERT(thread->IsSuspended() || thread->HasLaunchedSuspendAll());
thread->IterateWeakEcmaGlobalStorage(gcUpdateWeak, GCKind::SHARED_GC);
thread->GetEcmaVM()->ProcessSnapShotEnv(gcUpdateWeak);
const_cast<Heap*>(thread->GetEcmaVM()->GetHeap())->ResetTlab();
if (needClearCache) {
thread->ClearVMCachedConstantPool();
}
});
if (!concurrentProcessStringTable_) {
stringTableCleaner->JoinAndWaitSweepWeakRefTask(gcUpdateWeak);
}
sHeap_->GetSweeper()->PostTask(false);
if (concurrentProcessStringTable_) {
LOG_GC(DEBUG) << "process string table concurrently";
stringTableCleaner->PostConcurrentSweepWeakRefTask(gcUpdateWeak);
}
}
void SharedGC::Finish()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SharedGC::Finish", "");
TRACE_GC(GCStats::Scope::ScopeId::Finish, sHeap_->GetEcmaGCStats());
if (markingInProgress_) {
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ConcurrentMarker::Reset", "");
sHeap_->GetConcurrentMarker()->Reset(false);
} else {
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "WorkManager::Finish", "");
sWorkManager_->Finish();
}
sHeap_->Reclaim(TriggerGCType::SHARED_GC);
sHeap_->GetSweeper()->TryFillSweptRegion();
}
void SharedGC::UpdateRecordWeakReference()
{
auto processWeakReference = [](SharedGCWorkNodeHolder *holder) {
ProcessQueue *queue = holder->GetWeakReferenceQueue();
while (true) {
auto obj = queue->PopBack();
if (UNLIKELY(obj == nullptr)) {
break;
}
ObjectSlot slot(ToUintPtr(obj));
JSTaggedValue value(slot.GetTaggedType());
if (value.IsWeak()) {
auto header = value.GetTaggedWeakRef();
Region *objectRegion = Region::ObjectAddressToRange(header);
if (!objectRegion->Test(header)) {
slot.Clear();
}
}
}
};
auto totalThreadCount = common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() + 1;
for (uint32_t i = 0; i < totalThreadCount; i++) {
processWeakReference(sWorkManager_->GetSharedGCWorkNodeHolder(i));
}
sWorkManager_->ForEachExtraTemporaryWorkNodeHolder(processWeakReference);
}
void SharedGC::ResetWorkManager(SharedGCWorkManager *sWorkManager)
{
sWorkManager_ = sWorkManager;
}
}