* Copyright (c) 2026 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/global_gc.h"
#include "ecmascript/checkpoint/thread_state_transition.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/shared_heap/global_gc_marker.h"
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/mem/heap-inl.h"
#include "ecmascript/mem/shared_heap/shared_full_gc.h"
#include "ecmascript/mem/work_manager-inl.h"
#include "ecmascript/mem/region-inl.h"
namespace panda::ecmascript {
GlobalGC::GlobalGC(SharedHeap *sHeap)
: sHeap_(sHeap), workManager_(sHeap->GetGlobalGCWorkManager()), cleaner_(sHeap) {}
GlobalGC::~GlobalGC() = default;
void GlobalGC::RunPhases()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::RunPhases", "");
TRACE_GC(GCStats::Scope::ScopeId::GlobalGC, sHeap_->GetEcmaGCStats());
Initialize();
Mark();
Clean();
Finish();
TriggerSharedGC();
LOG_GC(INFO) << "GlobalGC: finished in " << sp.TotalSpentTime() << "ms";
}
void GlobalGC::Initialize()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::Initialize", "");
workManager_->Initialize();
Runtime::GetInstance()->GCIterateThreadList([](JSThread *iteratedThread) {
Heap *heap = iteratedThread->GetEcmaVM()->GetHeap();
heap->Prepare();
heap->GetConcurrentMarker()->Reset(true);
heap->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
current->ClearMarkGCBitset();
});
});
}
void GlobalGC::Mark()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::Mark", "");
TRACE_GC(GCStats::Scope::ScopeId::GlobalMark, sHeap_->GetEcmaGCStats());
auto *marker = sHeap_->GetGlobalGCMarker();
GlobalGCWorkNodeHolder *holder = workManager_->GetHolder(DAEMON_THREAD_INDEX);
Runtime::GetInstance()->GCIterateThreadList([&](JSThread *iteratedThread) {
Heap *heap = iteratedThread->GetEcmaVM()->GetHeap();
heap->SetGCType(TriggerGCType::GLOBAL_GC);
marker->MarkRoots(heap, holder);
holder->PushWorkNodeToGlobal();
});
marker->ProcessMarkStack(workManager_, DAEMON_THREAD_INDEX);
sHeap_->WaitGlobalGCMarkTaskFinished();
marker->MarkJitCodeMaps(workManager_, DAEMON_THREAD_INDEX);
sHeap_->WaitGlobalGCMarkTaskFinished();
LOG_GC(INFO) << "GlobalGC: mark " << sp.TotalSpentTime() << "ms";
}
void GlobalGC::Clean()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::Clean", "");
TRACE_GC(GCStats::Scope::ScopeId::GlobalClean, sHeap_->GetEcmaGCStats());
cleaner_.Clean();
RebuildFreeList();
LOG_GC(INFO) << "GlobalGC: clean " << sp.TotalSpentTime() << "ms";
}
void GlobalGC::Finish()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::Finish", "");
workManager_->Finish();
Runtime::GetInstance()->GCIterateThreadList([](JSThread *iteratedThread) {
Heap *heap = iteratedThread->GetEcmaVM()->GetHeap();
heap->EnumerateRegions([](Region *region) {
if (!region->InAppSpawnSpace()) {
region->ClearMarkGCBitset();
}
});
});
}
void GlobalGC::RebuildFreeList()
{
Runtime::GetInstance()->GCIterateThreadList([](JSThread *iteratedThread) {
Heap *heap = iteratedThread->GetEcmaVM()->GetHeap();
heap->GetOldSpace()->RebuildFreeList();
heap->GetNonMovableSpace()->RebuildFreeList();
});
}
void GlobalGC::TriggerSharedGC()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GlobalGC::TriggerSharedGC", "");
TRACE_GC(GCStats::Scope::ScopeId::GlobalSharedGC, sHeap_->GetEcmaGCStats());
size_t sizeBefore = sHeap_->GetHeapObjectSize();
sHeap_->GetSharedFullGC()->RunPhases();
size_t sizeAfter = sHeap_->GetHeapObjectSize();
freedSize_ = sizeBefore > sizeAfter ? sizeBefore - sizeAfter : 0;
LOG_GC(INFO) << "GlobalGC: shared gc " << sp.TotalSpentTime() << "ms, freed "
<< (freedSize_ / 1_KB) << "KB";
}
}