• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
16 #include "runtime/mem/gc/gc_trigger.h"
17 
18 #include <atomic>
19 
20 #include "libpandabase/macros.h"
21 #include "runtime/include/runtime.h"
22 #include "runtime/include/runtime_options.h"
23 #include "runtime/include/panda_vm.h"
24 #include "utils/logger.h"
25 
26 namespace panda::mem {
27 
28 static constexpr size_t PERCENT_100 = 100;
29 
30 GCTrigger::~GCTrigger() = default;
31 
GCTriggerHeap(MemStatsType * mem_stats)32 GCTriggerHeap::GCTriggerHeap(MemStatsType *mem_stats) : mem_stats_(mem_stats) {}
33 
GCTriggerHeap(MemStatsType * mem_stats,size_t min_heap_size,uint8_t percent_threshold,size_t min_extra_size,size_t max_extra_size,uint32_t skip_gc_times)34 GCTriggerHeap::GCTriggerHeap(MemStatsType *mem_stats, size_t min_heap_size, uint8_t percent_threshold,
35                              size_t min_extra_size, size_t max_extra_size, uint32_t skip_gc_times)
36     : mem_stats_(mem_stats), skip_gc_count_(skip_gc_times)
37 {
38     percent_threshold_ = percent_threshold;
39     min_extra_size_ = min_extra_size;
40     max_extra_size_ = max_extra_size;
41     // If we have min_heap_size < 100, we get false positives in IsGcTriggered, since we divide by 100 first
42     ASSERT(min_heap_size >= 100);
43     target_footprint_.store((min_heap_size / PERCENT_100) * percent_threshold_, std::memory_order_relaxed);
44     LOG(DEBUG, GC_TRIGGER) << "GCTriggerHeap created, min heap size " << min_heap_size << ", percent threshold "
45                            << percent_threshold << ", min_extra_size " << min_extra_size << ", max_extra_size "
46                            << max_extra_size;
47 }
48 
SetMinTargetFootprint(size_t target_size)49 void GCTriggerHeap::SetMinTargetFootprint(size_t target_size)
50 {
51     LOG(DEBUG, GC_TRIGGER) << "SetTempTargetFootprint target_footprint = " << target_size;
52     min_target_footprint_ = target_size;
53     target_footprint_.store(target_size, std::memory_order_relaxed);
54 }
55 
RestoreMinTargetFootprint()56 void GCTriggerHeap::RestoreMinTargetFootprint()
57 {
58     min_target_footprint_ = DEFAULT_MIN_TARGET_FOOTPRINT;
59 }
60 
ComputeNewTargetFootprint(const GCTask & task,size_t heap_size_before_gc,size_t heap_size)61 void GCTriggerHeap::ComputeNewTargetFootprint(const GCTask &task, size_t heap_size_before_gc, size_t heap_size)
62 {
63     GC *gc = Thread::GetCurrent()->GetVM()->GetGC();
64     if (gc->IsGenerational() && task.reason_ == GCTaskCause::YOUNG_GC_CAUSE) {
65         // we don't want to update heap-trigger on young-gc
66         return;
67     }
68     // Note: divide by 100 first to avoid overflow
69     size_t delta = (heap_size / PERCENT_100) * percent_threshold_;
70 
71     if (heap_size > heap_size_before_gc) {  // heap increased corresponding with previous gc
72         delta = std::min(delta, max_extra_size_);
73     } else {
74         // if heap was squeeze from 200mb to 100mb we want to set a target to 150mb, not just 100mb*percent_threshold_
75         delta = std::max(delta, (heap_size_before_gc - heap_size) / 2);
76     }
77     delta = std::max(delta, min_extra_size_);
78     size_t target = heap_size + delta;
79 
80     target_footprint_.store(target, std::memory_order_relaxed);
81 
82     LOG(DEBUG, GC_TRIGGER) << "ComputeNewTargetFootprint target_footprint = " << target;
83 }
84 
IsGcTriggered()85 bool GCTriggerHeap::IsGcTriggered()
86 {
87     if (skip_gc_count_ > 0) {
88         skip_gc_count_--;
89         return false;
90     }
91     size_t bytes_in_heap = mem_stats_->GetFootprintHeap();
92     if (UNLIKELY(bytes_in_heap >= target_footprint_.load(std::memory_order_relaxed))) {
93         LOG(DEBUG, GC_TRIGGER) << "GCTriggerHeap triggered";
94         return true;
95     }
96     return false;
97 }
98 
CreateGCTrigger(MemStatsType * mem_stats,const GCTriggerConfig & config,InternalAllocatorPtr allocator)99 GCTrigger *CreateGCTrigger(MemStatsType *mem_stats, const GCTriggerConfig &config, InternalAllocatorPtr allocator)
100 {
101     std::string_view gc_trigger_type = config.GetGCTriggerType();
102     uint32_t skip_gc_times = config.GetSkipStartupGcCount();
103 
104     constexpr size_t DEFAULT_HEAP_SIZE = 8_MB;
105     constexpr uint8_t DEFAULT_PERCENT_THRESHOLD = 10;
106     auto trigger_type = GCTriggerType::INVALID_TRIGGER;
107     if (gc_trigger_type == "heap-trigger-test") {
108         trigger_type = GCTriggerType::HEAP_TRIGGER_TEST;
109     } else if (gc_trigger_type == "heap-trigger") {
110         trigger_type = GCTriggerType::HEAP_TRIGGER;
111     } else if (gc_trigger_type == "debug") {
112         trigger_type = GCTriggerType::DEBUG;
113     } else if (gc_trigger_type == "no-gc-for-start-up") {
114         trigger_type = GCTriggerType::NO_GC_FOR_START_UP;
115     }
116     GCTrigger *ret {nullptr};
117     switch (trigger_type) {  // NOLINT(hicpp-multiway-paths-covered)
118         case GCTriggerType::HEAP_TRIGGER_TEST:
119             ret = allocator->New<GCTriggerHeap>(mem_stats);
120             break;
121         case GCTriggerType::HEAP_TRIGGER:
122             ret = allocator->New<GCTriggerHeap>(mem_stats, DEFAULT_HEAP_SIZE, DEFAULT_PERCENT_THRESHOLD,
123                                                 config.GetMinExtraHeapSize(), config.GetMaxExtraHeapSize());
124             break;
125         case GCTriggerType::NO_GC_FOR_START_UP:
126             ret = allocator->New<GCTriggerHeap>(mem_stats, DEFAULT_HEAP_SIZE, DEFAULT_PERCENT_THRESHOLD,
127                                                 config.GetMinExtraHeapSize(), config.GetMaxExtraHeapSize(),
128                                                 skip_gc_times);
129             break;
130         case GCTriggerType::DEBUG:
131             ret = allocator->New<GCTriggerDebug>(config.GetDebugStart());
132             break;
133         default:
134             LOG(FATAL, GC) << "Wrong GCTrigger type";
135             break;
136     }
137     return ret;
138 }
139 
GCStarted(size_t heap_size)140 void GCTriggerHeap::GCStarted([[maybe_unused]] size_t heap_size) {}
141 
GCFinished(const GCTask & task,size_t heap_size_before_gc,size_t heap_size)142 void GCTriggerHeap::GCFinished(const GCTask &task, size_t heap_size_before_gc, size_t heap_size)
143 {
144     ComputeNewTargetFootprint(task, heap_size_before_gc, heap_size);
145 }
146 
GetTargetFootprint()147 size_t GCTriggerHeap::GetTargetFootprint()
148 {
149     return target_footprint_.load(std::memory_order_relaxed);
150 }
151 
GCTriggerDebug(uint64_t debug_start)152 GCTriggerDebug::GCTriggerDebug(uint64_t debug_start) : debug_start_(debug_start)
153 {
154     LOG(DEBUG, GC_TRIGGER) << "GCTriggerDebug created";
155 }
156 
IsGcTriggered()157 bool GCTriggerDebug::IsGcTriggered()
158 {
159     bool ret = false;
160     static std::atomic<uint64_t> counter = 0;
161     LOG(DEBUG, GC_TRIGGER) << "GCTriggerDebug counter " << counter;
162     if (counter >= debug_start_) {
163         LOG(DEBUG, GC_TRIGGER) << "GCTriggerDebug triggered";
164         ret = true;
165     }
166     counter++;
167     return ret;
168 }
169 
GCStarted(size_t heap_size)170 void GCTriggerDebug::GCStarted([[maybe_unused]] size_t heap_size) {}
171 
GCFinished(const GCTask & task,size_t heap_size_before_gc,size_t heap_size)172 void GCTriggerDebug::GCFinished([[maybe_unused]] const GCTask &task, [[maybe_unused]] size_t heap_size_before_gc,
173                                 [[maybe_unused]] size_t heap_size)
174 {
175 }
176 
GetTargetFootprint()177 size_t GCTriggerDebug::GetTargetFootprint()
178 {
179     return 0;
180 }
181 
182 }  // namespace panda::mem
183