1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15#ifndef ECMASCRIPT_MEM_GC_TRIGGER_H 16#define ECMASCRIPT_MEM_GC_TRIGGER_H 17 18#include <atomic> 19#include <ctime> 20#include <chrono> 21 22#include "libpandabase/macros.h" 23#include "ecmascript/common.h" 24#include "ecmascript/mem/mem_common.h" 25#include "ecmascript/base/gc_ring_buffer.h" 26#include "ecmascript/mem/heap.h" 27#include "ecmascript/napi/include/jsnapi_expo.h" 28 29namespace panda::ecmascript { 30class Heap; 31class SharedHeap; 32class ConcurrentMarker; 33class MemController; 34class SharedMemController; 35class EcmaVM; 36 37class IdleGCTrigger { 38using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE; 39using Clock = std::chrono::high_resolution_clock; 40public: 41 explicit IdleGCTrigger(Heap *heap, SharedHeap *sHeap, JSThread *thread, bool logEnable = false) 42 : heap_(heap), 43 sHeap_(sHeap), 44 thread_(thread), 45 optionalLogEnabled_(logEnable) {}; 46 virtual ~IdleGCTrigger() = default; 47 48 bool IsIdleState() const 49 { 50 return idleState_.load(); 51 } 52 53 const char *GetGCTypeName(TRIGGER_IDLE_GC_TYPE gcType) const 54 { 55 switch (gcType) { 56 case TRIGGER_IDLE_GC_TYPE::FULL_GC: 57 return "full gc"; 58 case TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK: 59 return "shared concurrent mark"; 60 case TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC: 61 return "shared full gc"; 62 case TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK: 63 return "local concurrent mark"; 64 case TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK: 65 return "local remark"; 66 default: 67 return "UnknownType"; 68 } 69 } 70 71 bool IsPossiblePostGCTask(TRIGGER_IDLE_GC_TYPE gcType) const 72 { 73 uint8_t bit = static_cast<uint8_t>(gcType); 74 return (bit & gcTaskPostedState_) != bit; 75 } 76 77 void SetPostGCTask(TRIGGER_IDLE_GC_TYPE gcType) 78 { 79 uint8_t bit = static_cast<uint8_t>(gcType); 80 gcTaskPostedState_ = (gcTaskPostedState_ | bit); 81 } 82 83 void ClearPostGCTask(TRIGGER_IDLE_GC_TYPE gcType) 84 { 85 uint8_t bit = static_cast<uint8_t>(gcType); 86 gcTaskPostedState_ = (gcTaskPostedState_ & ~bit); 87 } 88 89 void SetTriggerGCTaskCallback(const TriggerGCTaskCallback& callback) 90 { 91 triggerGCTaskCallback_ = callback; 92 } 93 94 void NotifyVsyncIdleStart(); 95 void NotifyLooperIdleStart(int64_t timestamp, int idleTime); 96 void NotifyLooperIdleEnd(int64_t timestamp); 97 void TryTriggerHandleMarkFinished(); 98 void TryTriggerLocalConcurrentMark(); 99 bool TryTriggerIdleLocalOldGC(); 100 bool TryTriggerIdleSharedOldGC(); 101 bool ReachIdleLocalOldGCThresholds(); 102 bool ReachIdleSharedGCThresholds(); 103 void TryPostHandleMarkFinished(); 104 void TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE gcType); 105 bool CheckIdleYoungGC() const; 106 template<class T> 107 bool CheckIdleOrHintOldGC(const T *baseHeap) const 108 { 109 size_t heapAliveSizeAfterGC = baseHeap->GetHeapAliveSizeAfterGC(); 110 if (heapAliveSizeAfterGC == 0) { 111 return false; 112 } 113 size_t expectHeapSize = std::max(static_cast<size_t>(heapAliveSizeAfterGC * IDLE_SPACE_SIZE_MIN_INC_RATIO), 114 heapAliveSizeAfterGC + IDLE_SPACE_SIZE_MIN_INC_STEP); 115 LOG_ECMA_IF(optionalLogEnabled_, DEBUG) << "IdleGCTrigger: check old GC heapAliveSizeAfterGC:" 116 << heapAliveSizeAfterGC << ";expectHeapSize" << expectHeapSize 117 << "heapObjectSize" << baseHeap->GetHeapObjectSize(); 118 return baseHeap->GetHeapObjectSize() >= expectHeapSize; 119 } 120 121 template<class T> 122 bool CheckIdleOrHintFullGC(const T *baseHeap) const 123 { 124 size_t heapAliveSizeAfterGC = baseHeap->GetHeapAliveSizeAfterGC(); 125 size_t expectHeapSize = std::max(static_cast<size_t>(heapAliveSizeAfterGC * IDLE_SPACE_SIZE_MIN_INC_RATIO), 126 heapAliveSizeAfterGC + IDLE_SPACE_SIZE_MIN_INC_STEP_FULL); 127 LOG_GC(DEBUG) << "IdleGCTrigger: check full GC heapAliveSizeAfterGC:" << heapAliveSizeAfterGC 128 << ";expectHeapSize:" << expectHeapSize << ";heapObjectSize:" << baseHeap->GetHeapObjectSize(); 129 if (baseHeap->GetHeapObjectSize() >= expectHeapSize) { 130 return true; 131 } 132 size_t fragmentSizeAfterGC = baseHeap->GetFragmentSizeAfterGC(); 133 size_t heapBasicLoss = baseHeap->GetHeapBasicLoss(); 134 if (fragmentSizeAfterGC <= heapBasicLoss) { 135 return false; 136 } 137 size_t fragmentSize = fragmentSizeAfterGC - heapBasicLoss; 138 size_t expectFragmentSize = std::max(static_cast<size_t>((baseHeap->GetCommittedSize() - heapBasicLoss) * 139 IDLE_FRAGMENT_SIZE_RATIO), IDLE_MIN_EXPECT_RECLAIM_SIZE); 140 LOG_GC(DEBUG) << "IdleGCTrigger: check full GC fragmentSizeAfterGC:" << fragmentSizeAfterGC 141 << ";heapBasicLoss:" << heapBasicLoss << ";expectFragmentSize" << expectFragmentSize; 142 return fragmentSize >= expectFragmentSize; 143 } 144 145 template<class T> 146 bool HintGCInLowDegree(const T *baseHeap) const 147 { 148 return CheckIdleOrHintOldGC<T>(baseHeap); 149 } 150 151 template<class T> 152 bool HintGCInMiddleDegree(const T *baseHeap) const 153 { 154 return CheckIdleOrHintOldGC<T>(baseHeap); 155 } 156 157 template<class T> 158 bool HintGCInHighDegree(const T *baseHeap) const 159 { 160 return CheckIdleOrHintFullGC<T>(baseHeap); 161 } 162 163private: 164 void PostIdleGCTask(TRIGGER_IDLE_GC_TYPE gcType); 165 166 Heap *heap_ {nullptr}; 167 SharedHeap *sHeap_ {nullptr}; 168 JSThread *thread_ {nullptr}; 169 bool optionalLogEnabled_ {false}; 170 171 std::atomic<bool> idleState_ {false}; 172 uint8_t gcTaskPostedState_ {0}; 173 TriggerGCTaskCallback triggerGCTaskCallback_ {nullptr}; 174}; 175 176} 177 178#endif // ECMASCRIPT_MEM_GC_TRIGGER_H