* 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/full_gc-inl.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/js_weak_container.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/parallel_marker.h"
#include "ecmascript/mem/verification.h"
#include "ecmascript/runtime_call_id.h"
namespace panda::ecmascript {
FullGC::FullGC(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManager()) {}
void FullGC::RunPhases()
{
ASSERT("FullGC should be disabled" && !g_isEnableCMCGC);
if (Runtime::GetInstance()->IsInBackground()) {
common::Taskpool::GetCurrentTaskpool()->SetThreadPriority(common::PriorityMode::HIGH_PRIORITY_BACKGROUND);
} else {
common::Taskpool::GetCurrentTaskpool()->SetThreadPriority(common::PriorityMode::STW);
}
GCStats *gcStats = heap_->GetEcmaVM()->GetEcmaGCStats();
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, ("FullGC::RunPhases;GCReason"
+ std::to_string(static_cast<int>(gcStats->GetGCReason()))
+ ";Sensitive" + std::to_string(static_cast<int>(heap_->GetSensitiveStatus()))
+ ";IsInBackground" + std::to_string(Runtime::GetInstance()->IsInBackground())
+ ";Startup" + std::to_string(static_cast<int>(heap_->GetStartupStatus()))
#if USE_CMS_GC
+ ";Slot" + std::to_string(heap_->GetSlotSpace()->GetCommittedSize())
#else
+ ";Young" + std::to_string(heap_->GetNewSpace()->GetCommittedSize())
+ ";Old" + std::to_string(heap_->GetOldSpace()->GetCommittedSize())
#endif
+ ";huge" + std::to_string(heap_->GetHugeObjectSpace()->GetCommittedSize())
+ ";NonMov" + std::to_string(heap_->GetNonMovableSpace()->GetCommittedSize())
+ ";TotCommit" + std::to_string(heap_->GetCommittedSize())
+ ";ObjSizeBeforeSensitive"
+ std::to_string(heap_->GetRecordHeapObjectSizeBeforeSensitive())).c_str(), "");
TRACE_GC(GCStats::Scope::ScopeId::TotalGC, gcStats);
MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), FullGC_RunPhases);
if (heap_->CheckOngoingConcurrentMarking()) {
LOG_GC(DEBUG) << "FullGC after ConcurrentMarking";
heap_->GetConcurrentMarker()->Reset();
}
ASSERT(!heap_->GetJSThread()->IsConcurrentCopying());
ProcessSharedGCRSetWorkList();
Initialize();
Mark();
Sweep();
Finish();
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
LOG_ECMA(DEBUG) << "start verify post fullgc";
Verification(heap_, VerifyKind::VERIFY_SHARED_RSET_POST_FULL_GC).VerifyAll();
}
common::Taskpool::GetCurrentTaskpool()->SetThreadPriority(common::PriorityMode::FOREGROUND);
}
void FullGC::RunPhasesForAppSpawn()
{
auto marker = reinterpret_cast<CompressGCMarker*>(heap_->GetCompressGCMarker());
marker->SetAppSpawn(true);
RunPhases();
marker->SetAppSpawn(false);
}
void FullGC::Initialize()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "FullGC::Initialize", "");
TRACE_GC(GCStats::Scope::ScopeId::Initialize, heap_->GetEcmaVM()->GetEcmaGCStats());
heap_->Prepare();
auto callback = [](Region *current) {
if constexpr (G_USE_CMS_GC) {
ASSERT(current->AliveObject() == 0);
} else {
current->ResetAliveObject();
}
current->ClearOldToNewRSet();
};
heap_->EnumerateNonMovableRegions(callback);
heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
current->ClearMarkGCBitset();
current->ClearCrossRegionRSet();
});
if constexpr (G_USE_CMS_GC) {
heap_->GetSlotSpace()->PrepareCompact();
if constexpr (G_USE_STICKY_CMS_GC) {
heap_->ClearGCBitSetForCMS();
}
} else {
heap_->SwapNewSpace();
}
workManager_->Initialize(TriggerGCType::FULL_GC, ParallelGCTaskPhase::COMPRESS_HANDLE_GLOBAL_POOL_TASK);
heap_->GetCompressGCMarker()->Initialize();
}
void FullGC::MarkRoots()
{
CompressGCMarker *marker = static_cast<CompressGCMarker*>(heap_->GetCompressGCMarker());
FullGCRunner fullGCRunner(heap_, workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX), forAppSpawn_);
FullGCMarkRootVisitor &fullGCMarkRootVisitor = fullGCRunner.GetMarkRootVisitor();
marker->MarkRoots(fullGCMarkRootVisitor);
}
void FullGC::Mark()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "FullGC::Mark", "");
TRACE_GC(GCStats::Scope::ScopeId::Mark, heap_->GetEcmaVM()->GetEcmaGCStats());
MarkRoots();
CompressGCMarker *marker = static_cast<CompressGCMarker *>(heap_->GetCompressGCMarker());
marker->ProcessMarkStack(MAIN_THREAD_INDEX);
heap_->WaitRunningMarkTaskFinished();
marker->MarkJitCodeMap(MAIN_THREAD_INDEX);
marker->ProcessMarkStack(MAIN_THREAD_INDEX);
heap_->WaitRunningMarkTaskFinished();
bool prev = heap_->IsParallelGCEnabled();
heap_->SetParallelGCEnabled(false);
MarkUntilFixPoint();
heap_->SetParallelGCEnabled(prev);
}
void FullGC::MarkUntilFixPoint()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "FullGC::MarkUntilFixPoint", "");
CompressGCMarker *marker = static_cast<CompressGCMarker *>(heap_->GetCompressGCMarker());
WorkNodeHolder *holder = workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX);
FullGCRunner runner(heap_, holder, forAppSpawn_);
std::vector<WeakAggregate> pendingWeakAggregates;
ASSERT(holder->freshWeakAggregateWorkNodeWrapper_.IsLocalAndGlobalEmpty());
WeakAggregate weakAggregate;
while (holder->PopPendingWeakAggregate(&weakAggregate)) {
if (!runner.HandleWeakAggregate(weakAggregate)) {
pendingWeakAggregates.emplace_back(weakAggregate);
}
}
while (true) {
if (holder->markWorkNodeWrapper_.IsLocalAndGlobalEmpty()) {
return;
}
marker->ProcessMarkStack(MAIN_THREAD_INDEX);
marker->MarkJitCodeMap(MAIN_THREAD_INDEX);
while (holder->PopFreshWeakAggregate(&weakAggregate)) {
if (!runner.HandleWeakAggregate(weakAggregate)) {
pendingWeakAggregates.emplace_back(weakAggregate);
}
}
auto it = pendingWeakAggregates.begin();
while (it != pendingWeakAggregates.end()) {
if (runner.HandleWeakAggregate(*it)) {
*it = pendingWeakAggregates.back();
if (it + 1 == pendingWeakAggregates.end()) {
pendingWeakAggregates.pop_back();
it = pendingWeakAggregates.end();
} else {
pendingWeakAggregates.pop_back();
}
} else {
++it;
}
}
}
}
void FullGC::Sweep()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "FullGC::Sweep", "");
TRACE_GC(GCStats::Scope::ScopeId::Sweep, heap_->GetEcmaVM()->GetEcmaGCStats());
uint32_t totalThreadCount = 1;
if (heap_->IsParallelGCEnabled()) {
totalThreadCount += common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
}
for (uint32_t i = 0; i < totalThreadCount; i++) {
UpdateRecordWeakReference(i);
}
for (uint32_t i = 0; i < totalThreadCount; i++) {
UpdateRecordWeakLinkedHashMap(i);
}
WeakRootVisitor gcUpdateWeak = [this](TaggedObject *header) -> TaggedObject* {
Region *objectRegion = Region::ObjectAddressToRange(header);
if (UNLIKELY(objectRegion == nullptr)) {
LOG_GC(ERROR) << "FullGC updateWeakReference: region is nullptr, header is " << header;
return nullptr;
}
if (!HasEvacuated(objectRegion)) {
if (objectRegion->InSharedHeap() || objectRegion->Test(header)) {
return header;
}
return nullptr;
}
MarkWord markWord(header, RELAXED_LOAD);
if (markWord.IsForwardingAddress()) {
return markWord.ToForwardingAddress();
}
return nullptr;
};
heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak);
heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak);
heap_->GetEcmaVM()->ProcessSnapShotEnv(gcUpdateWeak);
heap_->GetEcmaVM()->GetJSThread()->UpdateJitCodeMapReference(gcUpdateWeak);
heap_->GetEcmaVM()->IterateWeakGlobalEnvList(gcUpdateWeak);
heap_->GetSweeper()->Sweep(TriggerGCType::FULL_GC);
heap_->GetSweeper()->PostTask(TriggerGCType::FULL_GC);
heap_->GetJSThread()->ClearYoungGlobalList();
heap_->GetJSThread()->ClearToBeDeletedNodes();
}
void FullGC::Finish()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "FullGC::Finish", "");
TRACE_GC(GCStats::Scope::ScopeId::Finish, heap_->GetEcmaVM()->GetEcmaGCStats());
if constexpr (!G_USE_CMS_GC) {
if (!forAppSpawn_) {
heap_->SwapOldSpace();
}
} else {
heap_->GetSlotSpace()->MergeToRegions();
}
workManager_->Finish();
if (forAppSpawn_) {
heap_->ResumeForAppSpawn();
} else {
heap_->Resume(FULL_GC);
}
heap_->GetSweeper()->TryFillSweptRegion();
heap_->SetFullMarkRequestedState(false);
}
bool FullGC::HasEvacuated(Region *region)
{
if (forAppSpawn_) {
return !region->InHugeObjectSpace() && !region->InReadOnlySpace() && !region->InNonMovableSpace() &&
!region->InSharedHeap();
}
if constexpr (G_USE_CMS_GC) {
return region->InSlotSpace();
}
return region->InYoungOrOldSpace();
}
void FullGC::SetForAppSpawn(bool flag)
{
forAppSpawn_ = flag;
}
void FullGC::ProcessSharedGCRSetWorkList()
{
TRACE_GC(GCStats::Scope::ScopeId::ProcessSharedGCRSetWorkList, heap_->GetEcmaVM()->GetEcmaGCStats());
heap_->ProcessSharedGCRSetWorkList();
}
void FullGC::UpdateRecordWeakReference(uint32_t threadId)
{
ProcessQueue *queue = workManager_->GetWorkNodeHolder(threadId)->GetWeakReferenceQueue();
while (true) {
auto obj = queue->PopBack();
if (UNLIKELY(obj == nullptr)) {
break;
}
ObjectSlot slot(ToUintPtr(obj));
JSTaggedValue value(slot.GetTaggedType());
auto header = value.GetTaggedWeakRef();
Region *objectRegion = Region::ObjectAddressToRange(header);
if (!HasEvacuated(objectRegion)) {
if (!objectRegion->InSharedHeap() && !objectRegion->Test(header)) {
slot.Clear();
}
} else {
MarkWord markWord(header, RELAXED_LOAD);
if (markWord.IsForwardingAddress()) {
TaggedObject *dst = markWord.ToForwardingAddress();
auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject();
slot.Update(weakRef);
} else {
slot.Update(static_cast<JSTaggedType>(JSTaggedValue::Undefined().GetRawData()));
}
}
}
}
void FullGC::UpdateRecordWeakLinkedHashMap(uint32_t threadId)
{
WeakLinkedHashMapProcessQueue *queue = workManager_->GetWorkNodeHolder(threadId)->GetWeakLinkedHashMapQueue();
JSThread *thread = heap_->GetJSThread();
while (true) {
TaggedObject *obj = queue->PopBack();
if (UNLIKELY(obj == nullptr)) {
break;
}
WeakLinkedHashMap *map = WeakLinkedHashMap::Cast(obj);
ASSERT(map->VerifyLayout());
int entries = map->NumberOfAllUsedElements();
for (int i = 0; i < entries; ++i) {
JSTaggedValue maybeKey = map->GetKey(thread, i);
if (maybeKey.IsUndefined()) {
map->RemoveEntryFromGCThread(i);
}
}
}
}
}