1 /** 2 * Copyright (c) 2021-2022 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 PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H 16 #define PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H 17 18 #include <cstddef> 19 #include <cstdint> 20 #include <string_view> 21 22 #include "libpandabase/macros.h" 23 #include "libpandabase/utils/ring_buffer.h" 24 #include "runtime/mem/gc/gc.h" 25 26 namespace panda { 27 28 class RuntimeOptions; 29 30 namespace test { 31 class GCTriggerTest; 32 } // namespace test 33 34 namespace mem { 35 36 enum class GCTriggerType { 37 INVALID_TRIGGER, 38 HEAP_TRIGGER_TEST, // TRIGGER with low thresholds for tests 39 HEAP_TRIGGER, // Standard TRIGGER with production ready thresholds 40 ADAPTIVE_HEAP_TRIGGER, // TRIGGER with adaptive strategy for heap increasing 41 NO_GC_FOR_START_UP, // A non-production strategy, TRIGGER GC after the app starts up 42 TRIGGER_HEAP_OCCUPANCY, // Trigger full GC by heap size threshold 43 DEBUG, // Debug TRIGGER which always returns true 44 DEBUG_NEVER, // Trigger for testing which never triggers (young-gc can still trigger), for test purpose 45 GCTRIGGER_LAST = DEBUG 46 }; 47 48 class GCTriggerConfig { 49 public: 50 GCTriggerConfig(const RuntimeOptions &options, panda_file::SourceLang lang); 51 GetGCTriggerType()52 std::string_view GetGCTriggerType() const 53 { 54 return gc_trigger_type_; 55 } 56 GetDebugStart()57 uint64_t GetDebugStart() const 58 { 59 return debug_start_; 60 } 61 GetPercentThreshold()62 uint32_t GetPercentThreshold() const 63 { 64 return percent_threshold_; 65 } 66 GetAdaptiveMultiplier()67 uint32_t GetAdaptiveMultiplier() const 68 { 69 return adaptive_multiplier_; 70 } 71 GetMinExtraHeapSize()72 size_t GetMinExtraHeapSize() const 73 { 74 return min_extra_heap_size_; 75 } 76 GetMaxExtraHeapSize()77 size_t GetMaxExtraHeapSize() const 78 { 79 return max_extra_heap_size_; 80 } 81 GetMaxTriggerPercent()82 uint32_t GetMaxTriggerPercent() const 83 { 84 return max_trigger_percent_; 85 } 86 GetSkipStartupGcCount()87 uint32_t GetSkipStartupGcCount() const 88 { 89 return skip_startup_gc_count_; 90 } 91 92 private: 93 std::string gc_trigger_type_; 94 uint64_t debug_start_; 95 uint32_t percent_threshold_; 96 uint32_t adaptive_multiplier_; 97 size_t min_extra_heap_size_; 98 size_t max_extra_heap_size_; 99 uint32_t max_trigger_percent_; 100 uint32_t skip_startup_gc_count_; 101 }; 102 103 class GCTrigger : public GCListener { 104 public: GCTrigger(HeapSpace * heap_space)105 explicit GCTrigger(HeapSpace *heap_space) : heap_space_(heap_space) {} 106 ~GCTrigger() override; 107 NO_COPY_SEMANTIC(GCTrigger); 108 NO_MOVE_SEMANTIC(GCTrigger); 109 110 /** 111 * \brief Checks if GC required 112 * @return returns true if GC should be executed 113 */ 114 virtual bool IsGcTriggered() = 0; SetMinTargetFootprint(size_t heap_size)115 virtual void SetMinTargetFootprint([[maybe_unused]] size_t heap_size) {} RestoreMinTargetFootprint()116 virtual void RestoreMinTargetFootprint() {} 117 118 protected: 119 HeapSpace *heap_space_ {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 120 private: 121 friend class GC; 122 }; 123 124 /** 125 * Triggers when heap increased by predefined % 126 */ 127 class GCTriggerHeap : public GCTrigger { 128 public: 129 explicit GCTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space); 130 explicit GCTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space, size_t min_heap_size, 131 uint8_t percent_threshold, size_t min_extra_size, size_t max_extra_size, 132 uint32_t skip_gc_times = 0); 133 134 bool IsGcTriggered() override; 135 136 void GCStarted(size_t heap_size) override; 137 void GCFinished(const GCTask &task, size_t heap_size_before_gc, size_t heap_size) override; 138 void SetMinTargetFootprint(size_t target_size) override; 139 void RestoreMinTargetFootprint() override; 140 void ComputeNewTargetFootprint(const GCTask &task, size_t heap_size_before_gc, size_t heap_size); 141 142 static constexpr uint8_t DEFAULT_PERCENTAGE_THRESHOLD = 20; 143 144 protected: 145 // TODO(dtrubenkov): change to the proper value when all triggers will be enabled 146 static constexpr size_t MIN_HEAP_SIZE_FOR_TRIGGER = 512; 147 static constexpr size_t DEFAULT_MIN_TARGET_FOOTPRINT = 256; 148 static constexpr size_t DEFAULT_MIN_EXTRA_HEAP_SIZE = 32; // For heap-trigger-test 149 static constexpr size_t DEFAULT_MAX_EXTRA_HEAP_SIZE = 512_KB; // For heap-trigger-test 150 151 virtual size_t ComputeTarget(size_t heap_size_before_gc, size_t heap_size); 152 153 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 154 std::atomic<size_t> target_footprint_ {MIN_HEAP_SIZE_FOR_TRIGGER}; 155 156 /** 157 * We'll trigger if heap increased by delta, delta = heap_size_after_last_gc * percent_threshold_ % 158 * And the constraint on delta is: min_extra_size_ <= delta <= max_extra_size_ 159 */ 160 uint8_t percent_threshold_ {DEFAULT_PERCENTAGE_THRESHOLD}; // NOLINT(misc-non-private-member-variables-in-classes) 161 size_t min_extra_size_ {DEFAULT_MIN_EXTRA_HEAP_SIZE}; // NOLINT(misc-non-private-member-variables-in-classes) 162 size_t max_extra_size_ {DEFAULT_MAX_EXTRA_HEAP_SIZE}; // NOLINT(misc-non-private-member-variables-in-classes) 163 164 private: 165 size_t min_target_footprint_ {DEFAULT_MIN_TARGET_FOOTPRINT}; 166 MemStatsType *mem_stats_; 167 uint8_t skip_gc_count_ {0}; 168 169 friend class panda::test::GCTriggerTest; 170 }; 171 172 /** 173 * Triggers when heap increased by adaptive strategy 174 */ 175 class GCAdaptiveTriggerHeap : public GCTriggerHeap { 176 public: 177 GCAdaptiveTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space, size_t min_heap_size, 178 uint8_t percent_threshold, uint32_t adaptive_multiplier, size_t min_extra_size, 179 size_t max_extra_size, uint32_t skip_gc_times = 0); 180 NO_COPY_SEMANTIC(GCAdaptiveTriggerHeap); 181 NO_MOVE_SEMANTIC(GCAdaptiveTriggerHeap); 182 ~GCAdaptiveTriggerHeap() override = default; 183 184 private: 185 static constexpr uint32_t DEFAULT_INCREASE_MULTIPLIER = 3U; 186 // We save last RECENT_THRESHOLDS_COUNT thresholds for detect too often triggering 187 static constexpr size_t RECENT_THRESHOLDS_COUNT = 3; 188 189 size_t ComputeTarget(size_t heap_size_before_gc, size_t heap_size) override; 190 191 RingBuffer<size_t, RECENT_THRESHOLDS_COUNT> recent_target_thresholds_; 192 uint32_t adaptive_multiplier_ {DEFAULT_INCREASE_MULTIPLIER}; 193 194 friend class panda::test::GCTriggerTest; 195 }; 196 197 /** 198 * Trigger always returns true after given start 199 */ 200 class GCTriggerDebug : public GCTrigger { 201 public: 202 explicit GCTriggerDebug(uint64_t debug_start, HeapSpace *heap_space); 203 204 bool IsGcTriggered() override; 205 206 void GCStarted(size_t heap_size) override; 207 void GCFinished(const GCTask &task, size_t heap_size_before_gc, size_t heap_size) override; 208 209 private: 210 uint64_t debug_start_ = 0; 211 }; 212 213 class GCTriggerHeapOccupancy : public GCTrigger { 214 public: 215 explicit GCTriggerHeapOccupancy(HeapSpace *heap_space, uint32_t max_trigger_percent); 216 217 bool IsGcTriggered() override; 218 219 void GCStarted([[maybe_unused]] size_t heap_size) override; 220 void GCFinished([[maybe_unused]] const GCTask &task, [[maybe_unused]] size_t heap_size_before_gc, 221 [[maybe_unused]] size_t heap_size) override; 222 223 private: 224 double max_trigger_percent_ = 0.0; 225 }; 226 227 class GCNeverTrigger : public GCTrigger { 228 public: GCNeverTrigger(HeapSpace * heap_space)229 explicit GCNeverTrigger(HeapSpace *heap_space) : GCTrigger(heap_space) {} 230 IsGcTriggered()231 bool IsGcTriggered() override 232 { 233 return false; 234 } 235 GCStarted(size_t heap_size)236 void GCStarted([[maybe_unused]] size_t heap_size) override {} GCFinished(const GCTask & task,size_t heap_size_before_gc,size_t heap_size)237 void GCFinished([[maybe_unused]] const GCTask &task, [[maybe_unused]] size_t heap_size_before_gc, 238 [[maybe_unused]] size_t heap_size) override 239 { 240 } 241 }; 242 243 GCTrigger *CreateGCTrigger(MemStatsType *mem_stats, HeapSpace *heap_space, const GCTriggerConfig &config, 244 InternalAllocatorPtr allocator); 245 246 } // namespace mem 247 } // namespace panda 248 249 #endif // PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H 250