diff --git a/common_components/common_runtime/base_runtime.cpp b/common_components/common_runtime/base_runtime.cpp index e63899e1c326997846cbf47036ea0b5043a7a402..eed63ae50e71df80ad5dcb3c1fc72f96c3d67019 100755 --- a/common_components/common_runtime/base_runtime.cpp +++ b/common_components/common_runtime/base_runtime.cpp @@ -157,14 +157,23 @@ void BaseRuntime::PreFork(ThreadHolder *holder) } } -void BaseRuntime::PostFork() +void BaseRuntime::PostFork([[maybe_unused]] bool enableWarmStartup) { HeapManager::StartRuntimeThreads(); #ifdef ENABLE_COLD_STARTUP_GC_POLICY - StartupStatusManager::OnAppStartup(); + if (!enableWarmStartup) { + StartupStatusManager::OnAppStartup(); + } #endif } +void BaseRuntime::NotifyWarmStart() +{ + if (!Heap::GetHeap().IsGcStarted() && !Heap::GetHeap().OnStartupEvent()) { + StartupStatusManager::OnAppStartup(); + } +} + void BaseRuntime::WriteRoot(void *obj) { Heap::GetBarrier().WriteRoot(reinterpret_cast(obj)); diff --git a/common_components/heap/collector/heuristic_gc_policy.cpp b/common_components/heap/collector/heuristic_gc_policy.cpp index bb0e169673fc8a917890fe4c71a9d53021c49be6..248a212f5d41947889054b208e2391ef30681080 100644 --- a/common_components/heap/collector/heuristic_gc_policy.cpp +++ b/common_components/heap/collector/heuristic_gc_policy.cpp @@ -28,6 +28,8 @@ void StartupStatusManager::OnAppStartup() Taskpool *threadPool = common::Taskpool::GetCurrentTaskpool(); threadPool->PostDelayedTask( std::make_unique(0, threadPool, STARTUP_DURATION_MS), STARTUP_DURATION_MS); + OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, + "SmartGC: app startup just finished, CMC FinishGCRestrainTask create", ""); } void HeuristicGCPolicy::Init() diff --git a/common_components/heap/collector/heuristic_gc_policy.h b/common_components/heap/collector/heuristic_gc_policy.h index 5ca15f4a1db3b08e3a5049393e551323b465052c..4b5ba71fb24beec85b25a1769e87ad2ea41f9987 100644 --- a/common_components/heap/collector/heuristic_gc_policy.h +++ b/common_components/heap/collector/heuristic_gc_policy.h @@ -130,7 +130,8 @@ public: bool OnStartupEvent() const { - return StartupStatusManager::GetStartupStatus() == StartupStatus::COLD_STARTUP; + return StartupStatusManager::GetStartupStatus() == StartupStatus::COLD_STARTUP || + StartupStatusManager::GetStartupStatus() == StartupStatus::COLD_STARTUP_PARTIALLY_FINISH; } StartupStatus GetStartupStatus() const; diff --git a/common_components/heap/collector/tests/heuristic_gc_policy_test.cpp b/common_components/heap/collector/tests/heuristic_gc_policy_test.cpp index 28c26da19f643a2381868be29b12810da3d49fb6..70a4ac1eabae4fe596887e5a6ff7a6e2146e3f62 100644 --- a/common_components/heap/collector/tests/heuristic_gc_policy_test.cpp +++ b/common_components/heap/collector/tests/heuristic_gc_policy_test.cpp @@ -13,11 +13,15 @@ * limitations under the License. */ +#include "common_components/common_runtime/base_runtime_param.h" #include "common_components/heap/allocator/allocator.h" #include "common_components/heap/allocator/region_space.h" +#include "common_components/heap/collector/collector_resources.h" #include "common_components/heap/collector/heuristic_gc_policy.h" #include "common_components/heap/heap.h" +#include "common_components/heap/heap_manager.h" #include "common_components/tests/test_helper.h" +#include "common_interfaces/heap/heap_allocator.h" using namespace common; namespace common::test { @@ -25,7 +29,9 @@ class HeuristicGCPolicyTest : public common::test::BaseTestWithScope { protected: static void SetUpTestCase() { - BaseRuntime::GetInstance()->Init(); + RuntimeParam param = BaseRuntimeParam::DefaultRuntimeParam(); + param.gcParam.enableGC = false; + BaseRuntime::GetInstance()->Init(param); } static void TearDownTestCase() @@ -197,4 +203,88 @@ HWTEST_F_L0(HeuristicGCPolicyTest, NotifyNativeAllocation_TriggerByBytes1) EXPECT_EQ(gcPolicy.GetNotifiedNativeSize(), initialNotified + NATIVE_IMMEDIATE_THRESHOLD + 1); EXPECT_NE(gcPolicy.GetNativeHeapThreshold(), initialObjects + 1); } + +HWTEST_F_L0(HeuristicGCPolicyTest, ShouldRestrainGCOnStartup) { + Heap& heap = Heap::GetHeap(); + HeuristicGCPolicy& heuristicGCPolicy = heap.GetHeuristicGCPolicy(); + heuristicGCPolicy.TryHeuristicGC(); + + StartupStatusManager::SetStartupStatus(StartupStatus::COLD_STARTUP); + bool result = heuristicGCPolicy.ShouldRestrainGCOnStartupOrSensitive(); + EXPECT_EQ(result, true); // cold Startup + + StartupStatusManager::SetStartupStatus(StartupStatus::COLD_STARTUP_PARTIALLY_FINISH); + result = heuristicGCPolicy.ShouldRestrainGCOnStartupOrSensitive(); + EXPECT_EQ(result, true); // cold partially Startup + + StartupStatusManager::SetStartupStatus(StartupStatus::COLD_STARTUP_FINISH); + result = heuristicGCPolicy.ShouldRestrainGCOnStartupOrSensitive(); + EXPECT_EQ(result, false); // cold partially Startup +} + +HWTEST_F_L0(HeuristicGCPolicyTest, ShouldRestrainGCInSensitive) { + Heap& heap = Heap::GetHeap(); + HeuristicGCPolicy& heuristicGCPolicy = heap.GetHeuristicGCPolicy(); + heuristicGCPolicy.TryIdleGC(); + bool result = heuristicGCPolicy.ShouldRestrainGCInSensitive(1 * MB); + EXPECT_EQ(result, false); // normal scene + + heap.NotifyHighSensitive(true); + GCStats& gcStates = heap.GetCollectorResources().GetGCStats(); + gcStates.shouldRequestYoung = true; + result = heuristicGCPolicy.ShouldRestrainGCInSensitive(1 * MB); + EXPECT_EQ(result, false); // sensitive scene + + gcStates.shouldRequestYoung = false; + result = heuristicGCPolicy.ShouldRestrainGCInSensitive(1 * MB); + EXPECT_EQ(result, true); // sensitive scene for young gc + + result = heuristicGCPolicy.ShouldRestrainGCInSensitive(200 * MB); + EXPECT_EQ(result, false); // sensitive scene for size over threshold + + heap.NotifyHighSensitive(false); + EXPECT_EQ(result, false); // exit sensitive +} + +HWTEST_F_L0(HeuristicGCPolicyTest, CheckAndTriggerHintGCLow) { + Heap& heap = Heap::GetHeap(); + HeuristicGCPolicy& heuristicGCPolicy = heap.GetHeuristicGCPolicy(); + bool result = heuristicGCPolicy.CheckAndTriggerHintGC(MemoryReduceDegree::LOW); + EXPECT_EQ(result, false); + + heap.NotifyHighSensitive(false); + result = heuristicGCPolicy.CheckAndTriggerHintGC(MemoryReduceDegree::LOW); + EXPECT_EQ(result, false); + + auto obj = common::HeapAllocator::AllocateLargeRegion(7 * MB); + RegionDesc *regionInfo = RegionDesc::GetAliveRegionDescAt(reinterpret_cast(obj)); + HeapAddress start = regionInfo->GetRegionAllocPtr(); + HeapAddress end = regionInfo->GetRegionEnd(); + constexpr uint64_t TAG_SPECIAL = 0x02ULL; + for (HeapAddress current = start; current < end; current += sizeof(HeapAddress)) { + *(reinterpret_cast(current)) = TAG_SPECIAL; + } + + heap.RecordAliveSizeAfterLastGC(1); + result = heuristicGCPolicy.CheckAndTriggerHintGC(MemoryReduceDegree::LOW); + EXPECT_EQ(result, true); +} + +HWTEST_F_L0(HeuristicGCPolicyTest, CheckAndTriggerHintGCHigh) { + Heap& heap = Heap::GetHeap(); + HeuristicGCPolicy& heuristicGCPolicy = heap.GetHeuristicGCPolicy(); + + auto obj = common::HeapAllocator::AllocateLargeRegion(2 * MB); + RegionDesc *regionInfo = RegionDesc::GetAliveRegionDescAt(reinterpret_cast(obj)); + HeapAddress start = regionInfo->GetRegionAllocPtr(); + HeapAddress end = regionInfo->GetRegionEnd(); + constexpr uint64_t TAG_SPECIAL = 0x02ULL; + for (HeapAddress current = start; current < end; current += sizeof(HeapAddress)) { + *(reinterpret_cast(current)) = TAG_SPECIAL; + } + + heap.RecordAliveSizeAfterLastGC(1); + bool result = heuristicGCPolicy.CheckAndTriggerHintGC(MemoryReduceDegree::HIGH); + EXPECT_EQ(result, true); +} } // namespace common::test \ No newline at end of file diff --git a/common_components/heap/heap.cpp b/common_components/heap/heap.cpp index 630c1fa1b59a023056a596e4779e5afdc6d7465a..93d9cc4fd2967cdb4b9a302f97dbb50995b9a666 100644 --- a/common_components/heap/heap.cpp +++ b/common_components/heap/heap.cpp @@ -99,7 +99,7 @@ public: void SetGCPhase(const GCPhase phase) override; Collector& GetCollector() override; Allocator& GetAllocator() override; - + HeuristicGCPolicy& GetHeuristicGCPolicy() override; size_t GetMaxCapacity() const override; size_t GetCurrentCapacity() const override; size_t GetUsedPageSize() const override; @@ -133,6 +133,7 @@ public: void SetRecordHeapObjectSizeBeforeSensitive(size_t objSize) override; AppSensitiveStatus GetSensitiveStatus() override; StartupStatus GetStartupStatus() override; + bool OnStartupEvent() const override; private: // allocator is actually a subspace in heap @@ -217,6 +218,11 @@ void HeapImpl::StopRuntimeThreads() collectorResources_.StopRuntimeThreads(); } +HeuristicGCPolicy& HeapImpl::GetHeuristicGCPolicy() +{ + return heuristicGCPolicy_; +} + void HeapImpl::TryHeuristicGC() { heuristicGCPolicy_.TryHeuristicGC(); @@ -295,6 +301,11 @@ StartupStatus HeapImpl::GetStartupStatus() return heuristicGCPolicy_.GetStartupStatus(); } +bool HeapImpl::OnStartupEvent() const +{ + return heuristicGCPolicy_.OnStartupEvent(); +} + Collector& HeapImpl::GetCollector() { return collectorProxy_.GetCurrentCollector(); } Allocator& HeapImpl::GetAllocator() { return *theSpace_; } diff --git a/common_components/heap/heap.h b/common_components/heap/heap.h index 1954a9e0d8fc1244164ae87f097e007ac44e331c..e3b548e9df4723dc834a4519f3d9e051c7fed311 100755 --- a/common_components/heap/heap.h +++ b/common_components/heap/heap.h @@ -34,7 +34,10 @@ class Allocator; class AllocationBuffer; class FinalizerProcessor; class CollectorResources; +class HeuristicGCPolicy; using MemoryReduceDegree = common::MemoryReduceDegree; +using AppSensitiveStatus = common::AppSensitiveStatus; +using StartupStatus = common::StartupStatus; class Heap { public: @@ -93,6 +96,7 @@ public: virtual Collector& GetCollector() = 0; virtual Allocator& GetAllocator() = 0; + virtual HeuristicGCPolicy& GetHeuristicGCPolicy() = 0; virtual void TryHeuristicGC() = 0; virtual void TryIdleGC() = 0; virtual void NotifyNativeAllocation(size_t bytes) = 0; @@ -108,6 +112,7 @@ public: virtual void SetRecordHeapObjectSizeBeforeSensitive(size_t objSize) = 0; virtual AppSensitiveStatus GetSensitiveStatus() = 0; virtual StartupStatus GetStartupStatus() = 0; + virtual bool OnStartupEvent() const = 0; /* to avoid misunderstanding, variant types of heap size are defined as followed: * |------------------------------ max capacity ---------------------------------| * |------------------------------ current capacity ------------------------| diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp index 218730d07d7664a86f97e20a58dcfbc9fabd21b3..b8fa05a65b9c3af9c17e2b3a24ab59564cc74e1a 100644 --- a/ecmascript/ecma_vm.cpp +++ b/ecmascript/ecma_vm.cpp @@ -174,7 +174,8 @@ void EcmaVM::PostFork(const JSRuntimeOptions &option) SharedHeap::GetInstance()->ResetLargeCapacity(); heap_->ResetLargeCapacity(); } - Runtime::GetInstance()->PostFork(); + bool enableWarmStartup = option.GetEnableWarmStartupSmartGC(); + Runtime::GetInstance()->PostFork(enableWarmStartup); Runtime::GetInstance()->SetPostForked(true); RandomGenerator::InitRandom(GetAssociatedJSThread()); heap_->SetHeapMode(HeapMode::SHARE); @@ -204,7 +205,7 @@ void EcmaVM::PostFork(const JSRuntimeOptions &option) GetJSOptions().SetArkProperties(arkProperties); #endif #ifdef ENABLE_POSTFORK_FORCEEXPAND - if (option.GetEnableWarmStartupSmartGC()) { + if (enableWarmStartup) { LOG_ECMA(WARN) << "SmartGC: process is start by premake/preload, skip cold start smrt gc." << " replace with warm startup smart gc later"; } else { diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index 9c1bc4066e6de9d84b5d3583e9ebd61408ee0573..4d8daad5f262f6e37e9f85db368df49a54979f68 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -2446,8 +2446,30 @@ void Heap::NotifyWarmStartup() ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SmartGC: warm startup GC restrain start", ""); // warm startup use the same GC restrain policy as cold startup LOG_GC(INFO) << "SmartGC: warm startup use the same GC restrain policy as cold startup"; - NotifyPostFork(); - NotifyFinishColdStartSoon(); + if (g_isEnableCMCGC) { + common::BaseRuntime::NotifyWarmStart(); + return; + } + if (AllowWarmStartGcRestrain()) { + NotifyPostFork(); + NotifyFinishColdStartSoon(); + } +} + +bool Heap::AllowWarmStartGcRestrain() +{ + bool hasFinishedMark = ecmaVm_->GetJSThread()->IsConcurrentMarkingOrFinished(); + if (hasFinishedMark) { + LOG_ECMA(WARN) << "SmartGC: app warm start gc, but gc mark is concurrent marking or finished, skip"; + return false; + } + StartupStatus status = GetStartupStatus(); + bool isGcRestraining = (status == StartupStatus::ON_STARTUP || status == StartupStatus::JUST_FINISH_STARTUP); + if (isGcRestraining) { + LOG_ECMA(WARN) << "SmartGC: app warm start gc, but it is already in GC restraining now, skip"; + return false; + } + return true; } void Heap::NotifyHighSensitive(bool isStart) diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index bd6db36ec50a4514a28fe8bcaaa9919a7a019b3e..b41ee5e6230ecc1ba34a06c3f9bf5c53dbee7306 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -1410,6 +1410,8 @@ public: void NotifyWarmStartup(); + bool AllowWarmStartGcRestrain(); + void NotifyHighSensitive(bool isStart); bool HandleExitHighSensitiveEvent(); diff --git a/ecmascript/napi/dfx_jsnapi.cpp b/ecmascript/napi/dfx_jsnapi.cpp index e377c9b54f91e19d3328f545d951bd7c1f3f9aa1..2781517a7bbd70c1e2f91a29d0234fd1b1a35694 100644 --- a/ecmascript/napi/dfx_jsnapi.cpp +++ b/ecmascript/napi/dfx_jsnapi.cpp @@ -624,32 +624,7 @@ void DFXJSNApi::NotifyWarmStart(EcmaVM *vm) { LOG_ECMA(INFO) << "SmartGC: app warm start gc"; ecmascript::ThreadManagedScope managedScope(vm->GetJSThread()); - if (AllowWarmStartGcRestrain(vm)) { - const_cast(vm->GetHeap())->NotifyWarmStartup(); - } -} - -bool DFXJSNApi::AllowWarmStartGcRestrain(EcmaVM *vm) -{ - common::GCPhase gcPhase = vm->GetJSThread()->GetCMCGCPhase(); - if (gcPhase > common::GCPhase::GC_PHASE_IDLE) { - LOG_ECMA(WARN) << "SmartGC: app warm start gc, but gc phase more than GC_PHASE_IDLE, skip"; - return false; - } - - bool hasFinishedMark = vm->GetJSThread()->IsConcurrentMarkingOrFinished(); - if (hasFinishedMark) { - LOG_ECMA(WARN) << "SmartGC: app warm start gc, but gc mark is concurrent marking or finished, skip"; - return false; - } - ecmascript::StartupStatus status = const_cast(vm->GetHeap())->GetStartupStatus(); - bool isGcRestraining = (status == ecmascript::StartupStatus::ON_STARTUP || - status == ecmascript::StartupStatus::JUST_FINISH_STARTUP); - if (isGcRestraining) { - LOG_ECMA(WARN) << "SmartGC: app warm start gc, but it is already in GC restraining now, skip"; - return false; - } - return true; + const_cast(vm->GetHeap())->NotifyWarmStartup(); } bool DFXJSNApi::StopCpuProfilerForColdStart([[maybe_unused]] const EcmaVM *vm) diff --git a/ecmascript/napi/include/dfx_jsnapi.h b/ecmascript/napi/include/dfx_jsnapi.h index 523bbb62e0f6fa3f43fc7f231a4e9271ea92c0a5..2c033ecc00e65a0fc073eb6003af1e9a95a6ab7c 100644 --- a/ecmascript/napi/include/dfx_jsnapi.h +++ b/ecmascript/napi/include/dfx_jsnapi.h @@ -114,7 +114,6 @@ public: static void NotifyFinishColdStart(EcmaVM *vm, [[maybe_unused]] bool isConvinced); static void NotifyHighSensitive(EcmaVM *vm, bool isStart); static void NotifyWarmStart(EcmaVM *vm); - static bool AllowWarmStartGcRestrain(EcmaVM *vm); static bool BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::vector& jsFrames); static int32_t GetObjectHash(const EcmaVM *vm, Local nativeObject); static int32_t GetObjectHashCode(const EcmaVM *vm, Local nativeObject); diff --git a/ecmascript/napi/test/dfx_jsnapi_tests.cpp b/ecmascript/napi/test/dfx_jsnapi_tests.cpp index d5b4deb3aa822ab042357129cae8e07f84420c4c..4d797bb93c7442c24f8f77635c58a15d41902f89 100644 --- a/ecmascript/napi/test/dfx_jsnapi_tests.cpp +++ b/ecmascript/napi/test/dfx_jsnapi_tests.cpp @@ -466,6 +466,33 @@ HWTEST_F_L0(DFXJSNApiTests, NotifyHighSensitive) EXPECT_TRUE(heap->GetSensitiveStatus() == AppSensitiveStatus::EXIT_HIGH_SENSITIVE); } +HWTEST_F_L0(DFXJSNApiTests, NotifyWarmStart) +{ + auto heap = const_cast(vm_->GetHeap()); + if (!g_isEnableCMCGC) { + DFXJSNApi::NotifyWarmStart(vm_); + EXPECT_TRUE(heap->OnStartupEvent()); + std::this_thread::sleep_for(std::chrono::seconds(3)); + if (!heap->OnStartupEvent()) { + StartupStatus startupStatus = heap->GetStartupStatus(); + EXPECT_TRUE(startupStatus == StartupStatus::JUST_FINISH_STARTUP); + } + } else { + DFXJSNApi::NotifyWarmStart(vm_); + EXPECT_TRUE(g_isEnableCMCGC); + } +} + +HWTEST_F_L0(DFXJSNApiTests, NotifyWarmStartCMC) +{ + if (!g_isEnableCMCGC) { + g_isEnableCMCGC = true; + DFXJSNApi::NotifyWarmStart(vm_); + EXPECT_TRUE(g_isEnableCMCGC); + g_isEnableCMCGC = false; + } +} + HWTEST_F_L0(DFXJSNApiTests, GetGCCount) { vm_->GetJSOptions().SetIsWorker(true); diff --git a/ecmascript/runtime.cpp b/ecmascript/runtime.cpp index 779564acd4d8814fbe84e4dc5b4f43085975f315..470d2a75aecff93a54de584cbc55b74e6bd26bcd 100644 --- a/ecmascript/runtime.cpp +++ b/ecmascript/runtime.cpp @@ -555,10 +555,10 @@ void Runtime::PreFork(JSThread *thread) } } -void Runtime::PostFork() +void Runtime::PostFork(bool enableWarmStartup) { if (g_isEnableCMCGC) { - baseInstance_->PostFork(); + baseInstance_->PostFork(enableWarmStartup); } } } // namespace panda::ecmascript diff --git a/ecmascript/runtime.h b/ecmascript/runtime.h index 0baf9bfa8efe974c0ed737dce7079d1d5627df2a..92a4984266c6492a3a993cb99f22353ff4df6cb4 100644 --- a/ecmascript/runtime.h +++ b/ecmascript/runtime.h @@ -270,7 +270,7 @@ public: } void PreFork(JSThread *thread); - void PostFork(); + void PostFork(bool enableWarmStartup); RawHeapDumpCropLevel GetRawHeapDumpCropLevel() const { diff --git a/ecmascript/tests/gc_second_test.cpp b/ecmascript/tests/gc_second_test.cpp index 82497a75e6a6fc7d293b2f7d555478d9d8f76e62..7d0f7998e3ee2477f3d13562003cd73ab98b817d 100644 --- a/ecmascript/tests/gc_second_test.cpp +++ b/ecmascript/tests/gc_second_test.cpp @@ -544,4 +544,19 @@ HWTEST_F_L0(GCTest, IncrementMarkerTest004) heap->GetIncrementalMarker()->TriggerIncrementalMark(100); } +HWTEST_F_L0(GCTest, NotifyWarmStartFalse001) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + EXPECT_TRUE(heap->AllowWarmStartGcRestrain()); + heap->TriggerConcurrentMarking(); + EXPECT_FALSE(heap->AllowWarmStartGcRestrain()); +} + +HWTEST_F_L0(GCTest, NotifyWarmStartFalse002) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + EXPECT_TRUE(heap->AllowWarmStartGcRestrain()); + heap->NotifyPostFork(); + EXPECT_FALSE(heap->AllowWarmStartGcRestrain()); +} } // namespace panda::test