From 1f9e21a744483d0ae9f78e811b577e6b0c370901 Mon Sep 17 00:00:00 2001 From: wuwanqi Date: Thu, 10 Aug 2023 10:22:53 +0800 Subject: [PATCH] Add force expand interface issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I7S293?from=project-issue Signed-off-by: wuwanqi Change-Id: I8d46728c381a956d3a14f036fe4c03ad4d0b4c8b --- BUILD.gn | 4 ++ ecmascript/ecma_vm.cpp | 3 + ecmascript/mem/heap.cpp | 83 ++++++++++++++++++++++++++-- ecmascript/mem/heap.h | 40 ++++++++++++++ ecmascript/mem/linear_space.cpp | 24 +++++++- ecmascript/mem/linear_space.h | 1 + ecmascript/mem/sparse_space.cpp | 2 +- ecmascript/napi/dfx_jsnapi.cpp | 14 +++++ ecmascript/napi/include/dfx_jsnapi.h | 2 + ecmascript/tests/gc_test.cpp | 55 ++++++++++++++++++ 10 files changed, 220 insertions(+), 8 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 51e758f443..fd5cfec43a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -931,6 +931,10 @@ ohos_source_set("libark_jsruntime_set") { "/system/lib64/${arkcompiler_relative_lib_path}/lib_ark_builtins.d.abc" defines += [ "TARGET_BUILTINS_DTS_PATH=\"${target_builtins_dts_path}\"" ] + if (current_cpu == "arm64") { + defines += [ "ENABLE_POSTFORK_FORCEEXPAND" ] + } + sources = ecma_source sources += ecma_profiler_source sources += ecma_debugger_source diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp index 3b036a3357..0dc12bae6b 100644 --- a/ecmascript/ecma_vm.cpp +++ b/ecmascript/ecma_vm.cpp @@ -143,6 +143,9 @@ void EcmaVM::PostFork() heap_->SetHeapMode(HeapMode::SHARE); GetAssociatedJSThread()->SetThreadId(); heap_->EnableParallelGC(); +#ifdef ENABLE_POSTFORK_FORCEEXPAND + heap_->NotifyPostFork(); +#endif } EcmaVM::EcmaVM(JSRuntimeOptions options, EcmaParamConfiguration config) diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index 6c6859f6db..191bb34eb0 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -726,8 +726,9 @@ bool Heap::CheckAndTriggerOldGC(size_t size) if (isFullMarking && oldSpace_->GetOvershootSize() == 0) { oldSpace_->SetOvershootSize(GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); } - if (isNativeSizeLargeTrigger || OldSpaceExceedLimit() || OldSpaceExceedCapacity(size) || - GetHeapObjectSize() > globalSpaceAllocLimit_ + oldSpace_->GetOvershootSize()) { + if ((isNativeSizeLargeTrigger || OldSpaceExceedLimit() || OldSpaceExceedCapacity(size) || + GetHeapObjectSize() > globalSpaceAllocLimit_ + oldSpace_->GetOvershootSize()) && + !NeedStopCollection()) { CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_LIMIT); if (!oldGCRequested_) { return true; @@ -853,8 +854,7 @@ void Heap::TryTriggerIncrementalMarking() double oldSpaceRemainSize = (oldSpaceAllocToLimitDuration - oldSpaceMarkDuration) * oldSpaceAllocSpeed; // mark finished before allocate limit - if ((oldSpaceRemainSize > 0 && oldSpaceRemainSize < DEFAULT_REGION_SIZE) || - GetHeapObjectSize() >= globalSpaceAllocLimit_) { + if ((oldSpaceRemainSize < DEFAULT_REGION_SIZE) || GetHeapObjectSize() >= globalSpaceAllocLimit_) { // The object allocated in incremental marking should lower than limit, // otherwise select trigger concurrent mark. size_t allocateSize = oldSpaceAllocSpeed * oldSpaceMarkDuration; @@ -1112,6 +1112,74 @@ void Heap::NotifyMemoryPressure(bool inHighMemoryPressure) } } +void Heap::NotifyFinishColdStart(bool isMainThread) +{ + { + os::memory::LockHolder holder(finishColdStartMutex_); + if (!onStartupEvent_) { + return; + } + + // set overshoot size to increase gc threashold larger 8MB than current heap size. + int64_t semiRemainSize = GetNewSpace()->GetInitialCapacity() - GetNewSpace()->GetCommittedSize(); + int64_t overshootSize = GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize() - semiRemainSize; + // overshoot size should be larger than 0. + GetNewSpace()->SetOverShootSize(std::max(overshootSize, (int64_t)0)); + GetNewSpace()->SetWaterLineWithoutGC(); + onStartupEvent_ = false; + } + + if (isMainThread) { + markType_ = MarkType::MARK_FULL; + TriggerConcurrentMarking(); + } +} + +void Heap::NotifyFinishColdStartSoon() +{ + if (!onStartupEvent_) { + return; + } + + // post 2s task + Taskpool::GetCurrentTaskpool()->PostTask( + std::make_unique(GetJSThread()->GetThreadId(), this)); +} + +void Heap::NotifyHighSensitive(bool isStart) +{ + onHighSensitiveEvent_ = isStart; + if (!onHighSensitiveEvent_ && !onStartupEvent_) { + // set overshoot size to increase gc threashold larger 8MB than current heap size. + int64_t semiRemainSize = GetNewSpace()->GetInitialCapacity() - GetNewSpace()->GetCommittedSize(); + int64_t overshootSize = GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize() - semiRemainSize; + // overshoot size should be larger than 0. + GetNewSpace()->SetOverShootSize(std::max(overshootSize, (int64_t)0)); + GetNewSpace()->SetWaterLineWithoutGC(); + + TryTriggerIncrementalMarking(); + TryTriggerIdleCollection(); + TryTriggerConcurrentMarking(); + } +} + +bool Heap::NeedStopCollection() +{ + if (!InSensitiveStatus()) { + return false; + } + + if (GetHeapObjectSize() < ecmaVm_->GetEcmaParamConfiguration().GetMaxHeapSize() - + ecmaVm_->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()) { + return true; + } + + GetNewSpace()->SetOverShootSize( + GetNewSpace()->GetCommittedSize() - GetNewSpace()->GetInitialCapacity() + + ecmaVm_->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); + return false; +} + bool Heap::CheckCanDistributeTask() { os::memory::LockHolder holder(waitTaskFinishedMutex_); @@ -1167,6 +1235,13 @@ bool Heap::AsyncClearTask::Run([[maybe_unused]] uint32_t threadIndex) return true; } +bool Heap::FinishColdStartTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + usleep(2000000); // 2000000 means 2s + heap_->NotifyFinishColdStart(false); + return true; +} + size_t Heap::GetArrayBufferSize() const { size_t result = 0; diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index 7345eba42c..fed9e7e55e 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -224,6 +224,17 @@ public: return memController_; } + bool InSensitiveStatus() const + { + return onHighSensitiveEvent_ || onStartupEvent_; + } + + void NotifyPostFork() + { + os::memory::LockHolder holder(finishColdStartMutex_); + onStartupEvent_ = true; + } + /* * For object allocations. */ @@ -405,6 +416,19 @@ public: void ClearIdleTask(); + bool IsEmptyIdleTask() const + { + return idleTask_ == IdleTaskType::NO_TASK; + } + + void NotifyFinishColdStart(bool isMainThread = true); + + void NotifyFinishColdStartSoon(); + + void NotifyHighSensitive(bool isStart); + + bool NeedStopCollection(); + #if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) void StartHeapTracking() { @@ -587,6 +611,19 @@ private: TriggerGCType gcType_; }; + class FinishColdStartTask : public Task { + public: + FinishColdStartTask(int32_t id, Heap *heap) + : Task(id), heap_(heap) {} + ~FinishColdStartTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(FinishColdStartTask); + NO_MOVE_SEMANTIC(FinishColdStartTask); + private: + Heap *heap_; + }; + EcmaVM *ecmaVm_ {nullptr}; JSThread *thread_ {nullptr}; @@ -683,6 +720,7 @@ private: uint32_t maxMarkTaskCount_ {0}; // parallel evacuator task number. uint32_t maxEvacuateTaskCount_ {0}; + os::memory::Mutex finishColdStartMutex_; os::memory::Mutex waitTaskFinishedMutex_; os::memory::ConditionVariable waitTaskFinishedCV_; @@ -700,6 +738,8 @@ private: bool inBackground_ {false}; IdleNotifyStatusCallback notifyIdleStatusCallback {nullptr}; + bool onHighSensitiveEvent_ {false}; + bool onStartupEvent_ {false}; IdleTaskType idleTask_ {IdleTaskType::NO_TASK}; float idlePredictDuration_ {0.0f}; diff --git a/ecmascript/mem/linear_space.cpp b/ecmascript/mem/linear_space.cpp index f614c914cc..fe03624f9c 100644 --- a/ecmascript/mem/linear_space.cpp +++ b/ecmascript/mem/linear_space.cpp @@ -43,13 +43,13 @@ uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted) return object; } if (Expand(isPromoted)) { - if (!isPromoted) { + if (!isPromoted && !heap_->NeedStopCollection()) { heap_->TryTriggerIncrementalMarking(); heap_->TryTriggerIdleCollection(); heap_->TryTriggerConcurrentMarking(); } object = allocator_.Allocate(size); - } else if (heap_->GetJSThread()->IsMarking()) { + } else if (heap_->GetJSThread()->IsMarking() || !heap_->IsEmptyIdleTask()) { // Temporary adjust semi space capacity if (heap_->IsFullMark()) { overShootSize_ = heap_->GetOldSpace()->GetMaximumCapacity() - heap_->GetOldSpace()->GetInitialCapacity(); @@ -71,7 +71,8 @@ uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted) bool LinearSpace::Expand(bool isPromoted) { - if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_) { + if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_ && + !heap_->NeedStopCollection()) { return false; } @@ -221,6 +222,23 @@ void SemiSpace::SetWaterLine() } } +void SemiSpace::SetWaterLineWithoutGC() +{ + waterLine_ = allocator_.GetTop(); + Region *last = GetCurrentRegion(); + if (last != nullptr) { + last->SetGCFlag(RegionGCFlags::HAS_AGE_MARK); + + EnumerateRegions([&last](Region *current) { + if (current != last) { + current->SetGCFlag(RegionGCFlags::BELOW_AGE_MARK); + } + }); + survivalObjectSize_ += allocateAfterLastGC_; + } + allocateAfterLastGC_ = 0; +} + size_t SemiSpace::GetHeapObjectSize() const { return survivalObjectSize_ + allocateAfterLastGC_; diff --git a/ecmascript/mem/linear_space.h b/ecmascript/mem/linear_space.h index 4e3b9126e5..f1f383200e 100644 --- a/ecmascript/mem/linear_space.h +++ b/ecmascript/mem/linear_space.h @@ -68,6 +68,7 @@ public: void SetOverShootSize(size_t size); bool AdjustCapacity(size_t allocatedSizeSinceGC); void SetWaterLine(); + void SetWaterLineWithoutGC(); uintptr_t GetWaterLine() const { diff --git a/ecmascript/mem/sparse_space.cpp b/ecmascript/mem/sparse_space.cpp index d587a15776..7277b972ea 100644 --- a/ecmascript/mem/sparse_space.cpp +++ b/ecmascript/mem/sparse_space.cpp @@ -551,7 +551,7 @@ void LocalSpace::Stop() } } -uintptr_t NonMovableSpace::CheckAndAllocate(size_t size) +uintptr_t NonMovableSpace::CheckAndAllocate(size_t size) { if (maximumCapacity_ == committedSize_ && GetHeapObjectSize() > MAX_NONMOVABLE_LIVE_OBJ_SIZE && !heap_->GetOldGCRequested()) diff --git a/ecmascript/napi/dfx_jsnapi.cpp b/ecmascript/napi/dfx_jsnapi.cpp index 76e973b19c..f21e55ad9d 100644 --- a/ecmascript/napi/dfx_jsnapi.cpp +++ b/ecmascript/napi/dfx_jsnapi.cpp @@ -270,6 +270,20 @@ void DFXJSNApi::NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure) const_cast(vm->GetHeap())->NotifyMemoryPressure(inHighMemoryPressure); } +void DFXJSNApi::NotifyFinishColdStart(EcmaVM *vm, bool isConvinced) +{ + if (isConvinced) { + const_cast(vm->GetHeap())->NotifyFinishColdStart(); + } else { + const_cast(vm->GetHeap())->NotifyFinishColdStartSoon(); + } +} + +void DFXJSNApi::NotifyHighSensitive(EcmaVM *vm, bool isStart) +{ + const_cast(vm->GetHeap())->NotifyHighSensitive(isStart); +} + bool DFXJSNApi::StopCpuProfilerForColdStart([[maybe_unused]] const EcmaVM *vm) { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) diff --git a/ecmascript/napi/include/dfx_jsnapi.h b/ecmascript/napi/include/dfx_jsnapi.h index f0702f728e..c2db828c80 100644 --- a/ecmascript/napi/include/dfx_jsnapi.h +++ b/ecmascript/napi/include/dfx_jsnapi.h @@ -75,6 +75,8 @@ public: static void NotifyIdleStatusControl(const EcmaVM *vm, std::function callback); static void NotifyIdleTime(const EcmaVM *vm, int idleMicroSec); static void NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure); + static void NotifyFinishColdStart(EcmaVM *vm, bool isConvinced); + static void NotifyHighSensitive(EcmaVM *vm, bool isStart); static bool BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::vector& jsFrames); // cpuprofiler diff --git a/ecmascript/tests/gc_test.cpp b/ecmascript/tests/gc_test.cpp index f4888be064..d6aa6a8fc3 100644 --- a/ecmascript/tests/gc_test.cpp +++ b/ecmascript/tests/gc_test.cpp @@ -307,6 +307,61 @@ HWTEST_F_L0(GCTest, ArkToolsForceFullGC) ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize); } +HWTEST_F_L0(GCTest, ColdStartForceExpand) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + size_t originalHeapSize = heap->GetCommittedSize(); + heap->GetConcurrentMarker()->ConfigConcurrentMark(false); + heap->NotifyPostFork(); + heap->NotifyFinishColdStartSoon(); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 500; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + usleep(2500000); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 100; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + EXPECT_TRUE(originalHeapSize < expandHeapSize); + EXPECT_TRUE(expandHeapSize > newSize); +} + +HWTEST_F_L0(GCTest, HighSensitiveForceExpand) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + size_t originalHeapSize = heap->GetCommittedSize(); + heap->GetConcurrentMarker()->ConfigConcurrentMark(false); + heap->NotifyHighSensitive(true); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 500; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + const_cast(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 100; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + EXPECT_TRUE(originalHeapSize < expandHeapSize); + EXPECT_TRUE(expandHeapSize > newSize); +} + HWTEST_F_L0(GCTest, NoFullConcurrentMarkOldGCTrigger) { #ifdef NDEBUG