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
16 #include "runtime/mem/gc/stw-gc/stw-gc.h"
17
18 #include "libpandabase/trace/trace.h"
19 #include "libpandabase/utils/logger.h"
20 #include "runtime/include/object_header-inl.h"
21 #include "runtime/include/runtime.h"
22 #include "runtime/include/runtime_notification.h"
23 #include "runtime/include/hclass.h"
24 #include "runtime/include/panda_vm.h"
25 #include "runtime/mem/gc/gc_root-inl.h"
26 #include "runtime/mem/gc/gc_workers_thread_pool.h"
27 #include "runtime/mem/heap_manager.h"
28 #include "runtime/mem/object_helpers.h"
29 #include "runtime/mem/rendezvous.h"
30 #include "runtime/mem/refstorage/global_object_storage.h"
31 #include "runtime/include/coretypes/class.h"
32 #include "runtime/mem/pygote_space_allocator-inl.h"
33 #include "runtime/mem/gc/static/gc_marker_static-inl.h"
34 #include "runtime/mem/gc/dynamic/gc_marker_dynamic-inl.h"
35
36 namespace panda::mem {
37
38 template <class LanguageConfig>
StwGC(ObjectAllocatorBase * object_allocator,const GCSettings & settings)39 StwGC<LanguageConfig>::StwGC(ObjectAllocatorBase *object_allocator, const GCSettings &settings)
40 : GCLang<LanguageConfig>(object_allocator, settings), marker_(this)
41 {
42 this->SetType(GCType::STW_GC);
43 }
44
45 template <class LanguageConfig>
InitializeImpl()46 void StwGC<LanguageConfig>::InitializeImpl()
47 {
48 trace::ScopedTrace scoped_trace(__FUNCTION__);
49 InternalAllocatorPtr allocator = this->GetInternalAllocator();
50 auto barrier_set = allocator->New<GCDummyBarrierSet>(allocator);
51 ASSERT(barrier_set != nullptr);
52 this->SetGCBarrierSet(barrier_set);
53 if (this->IsWorkerThreadsExist()) {
54 auto thread_pool = allocator->New<GCWorkersThreadPool>(allocator, this, this->GetSettings()->GCWorkersCount());
55 ASSERT(thread_pool != nullptr);
56 this->SetWorkersPool(thread_pool);
57 }
58 LOG_DEBUG_GC << "STW GC Initialized";
59 }
60
61 template <class LanguageConfig>
RunPhasesImpl(GCTask & task)62 void StwGC<LanguageConfig>::RunPhasesImpl(GCTask &task)
63 {
64 trace::ScopedTrace scoped_trace(__FUNCTION__);
65 GCScopedPauseStats scoped_pause_stats(this->GetPandaVm()->GetGCStats(), this->GetStats());
66 [[maybe_unused]] size_t bytes_in_heap_before_gc = this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
67 marker_.BindBitmaps(true);
68 Mark(task);
69 SweepStringTable();
70 Sweep();
71 marker_.ReverseMark();
72 [[maybe_unused]] size_t bytes_in_heap_after_gc = this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
73 ASSERT(bytes_in_heap_after_gc <= bytes_in_heap_before_gc);
74 task.collection_type_ = GCCollectionType::FULL;
75 }
76
77 template <class LanguageConfig>
Mark(const GCTask & task)78 void StwGC<LanguageConfig>::Mark(const GCTask &task)
79 {
80 GCScope<TRACE_PHASE> gc_scope(__FUNCTION__, this, GCPhase::GC_PHASE_MARK);
81
82 // Iterate over roots and add other roots
83 bool use_gc_workers = this->GetSettings()->ParallelMarkingEnabled();
84 GCMarkingStackType objects_stack(this, use_gc_workers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
85 use_gc_workers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
86 GCWorkersTaskTypes::TASK_MARKING);
87
88 this->VisitRoots(
89 [&objects_stack, &use_gc_workers, this](const GCRoot &gc_root) {
90 LOG_DEBUG_GC << "Handle root " << GetDebugInfoAboutObject(gc_root.GetObjectHeader());
91 if (marker_.MarkIfNotMarked(gc_root.GetObjectHeader())) {
92 objects_stack.PushToStack(gc_root.GetType(), gc_root.GetObjectHeader());
93 }
94 if (!use_gc_workers) {
95 MarkStack(&objects_stack, []([[maybe_unused]] const ObjectHeader *obj) { return true; });
96 }
97 },
98 VisitGCRootFlags::ACCESS_ROOT_ALL);
99
100 this->GetPandaVm()->VisitStringTable(
101 [this, &objects_stack](ObjectHeader *str) {
102 if (this->marker_.MarkIfNotMarked(str)) {
103 ASSERT(str != nullptr);
104 objects_stack.PushToStack(RootType::STRING_TABLE, str);
105 }
106 },
107 VisitGCRootFlags::ACCESS_ROOT_ALL);
108 MarkStack(&objects_stack, []([[maybe_unused]] const ObjectHeader *obj) { return true; });
109 ASSERT(objects_stack.Empty());
110 if (use_gc_workers) {
111 this->GetWorkersPool()->WaitUntilTasksEnd();
112 }
113 auto ref_clear_pred = [this]([[maybe_unused]] const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
114 // NOLINTNEXTLINE(performance-unnecessary-value-param)
115 this->GetPandaVm()->HandleReferences(task, ref_clear_pred);
116 }
117
118 template <class LanguageConfig>
MarkStack(GCMarkingStackType * stack,const GC::MarkPredicate & markPredicate)119 void StwGC<LanguageConfig>::MarkStack(GCMarkingStackType *stack, const GC::MarkPredicate &markPredicate)
120 {
121 trace::ScopedTrace scoped_trace(__FUNCTION__);
122 ASSERT(stack != nullptr);
123 size_t objects_count = 0;
124 auto ref_pred = [this](const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
125 while (!stack->Empty()) {
126 objects_count++;
127 auto *object = this->PopObjectFromStack(stack);
128 ValidateObject(nullptr, object);
129 auto *base_class = object->template ClassAddr<BaseClass>();
130 LOG_DEBUG_GC << "Current object: " << GetDebugInfoAboutObject(object);
131 if (markPredicate) {
132 this->marker_.MarkInstance(stack, ref_pred, object, base_class);
133 }
134 }
135 LOG_DEBUG_GC << "Iterated over " << objects_count << " objects in the stack";
136 }
137
138 template <class LanguageConfig>
SweepStringTable()139 void StwGC<LanguageConfig>::SweepStringTable()
140 {
141 GCScope<TRACE_PHASE> gc_scope(__FUNCTION__, this, GCPhase::GC_PHASE_SWEEP_STRING_TABLE);
142 auto vm = this->GetPandaVm();
143
144 if (!marker_.IsReverseMark()) {
145 LOG_DEBUG_GC << "SweepStringTable with MarkChecker";
146 vm->SweepStringTable([this](ObjectHeader *object) { return this->marker_.MarkChecker(object); });
147 } else {
148 LOG_DEBUG_GC << "SweepStringTable with ReverseMarkChecker";
149 vm->SweepStringTable([this](ObjectHeader *object) { return this->marker_.template MarkChecker<true>(object); });
150 }
151 }
152
153 template <class LanguageConfig>
Sweep()154 void StwGC<LanguageConfig>::Sweep()
155 {
156 GCScope<TRACE_PHASE> gc_scope(__FUNCTION__, this, GCPhase::GC_PHASE_SWEEP);
157
158 // TODO(dtrubenk): add other allocators when they will be used/implemented
159 if (!marker_.IsReverseMark()) {
160 LOG_DEBUG_GC << "Sweep with MarkChecker";
161 this->GetObjectAllocator()->Collect(
162 [this](ObjectHeader *object) {
163 auto status = this->marker_.MarkChecker(object);
164 if (status == ObjectStatus::DEAD_OBJECT) {
165 LOG_DEBUG_OBJECT_EVENTS << "DELETE OBJECT: " << object;
166 }
167 return status;
168 },
169 GCCollectMode::GC_ALL);
170 this->GetObjectAllocator()->VisitAndRemoveFreePools(
171 [](void *mem, [[maybe_unused]] size_t size) { PoolManager::GetMmapMemPool()->FreePool(mem, size); });
172
173 } else {
174 LOG_DEBUG_GC << "Sweep with ReverseMarkChecker";
175 this->GetObjectAllocator()->Collect(
176 [this](ObjectHeader *object) {
177 auto status = this->marker_.template MarkChecker<true>(object);
178 if (status == ObjectStatus::DEAD_OBJECT) {
179 LOG_DEBUG_OBJECT_EVENTS << "DELETE OBJECT: " << object;
180 }
181 return status;
182 },
183 GCCollectMode::GC_ALL);
184 this->GetObjectAllocator()->VisitAndRemoveFreePools(
185 [](void *mem, [[maybe_unused]] size_t size) { PoolManager::GetMmapMemPool()->FreePool(mem, size); });
186 }
187 }
188
189 // NOLINTNEXTLINE(misc-unused-parameters)
190 template <class LanguageConfig>
WaitForGC(GCTask task)191 void StwGC<LanguageConfig>::WaitForGC(GCTask task)
192 {
193 trace::ScopedTrace scoped_trace(__FUNCTION__);
194 Runtime::GetCurrent()->GetNotificationManager()->GarbageCollectorStartEvent();
195 // Atomic with acquire order reason: data race with gc_counter_ with dependecies on reads after the load which
196 // should become visible
197 auto old_counter = this->gc_counter_.load(std::memory_order_acquire);
198 this->GetPandaVm()->GetRendezvous()->SafepointBegin();
199
200 // Atomic with acquire order reason: data race with gc_counter_ with dependecies on reads after the load which
201 // should become visible
202 auto new_counter = this->gc_counter_.load(std::memory_order_acquire);
203 // Atomic with acquire order reason: data race with last_cause_ with dependecies on reads after the load which
204 // should become visible
205 if (new_counter > old_counter && this->last_cause_.load(std::memory_order_acquire) >= task.reason_) {
206 this->GetPandaVm()->GetRendezvous()->SafepointEnd();
207 return;
208 }
209 auto mem_stats = this->GetPandaVm()->GetMemStats();
210 mem_stats->RecordGCPauseStart();
211 // Create a copy of the constant GCTask to be able to change its value
212 this->RunPhases(task);
213 mem_stats->RecordGCPauseEnd();
214 this->GetPandaVm()->GetRendezvous()->SafepointEnd();
215 Runtime::GetCurrent()->GetNotificationManager()->GarbageCollectorFinishEvent();
216 this->GetPandaVm()->HandleGCFinished();
217 this->GetPandaVm()->HandleEnqueueReferences();
218 }
219
220 template <class LanguageConfig>
InitGCBits(panda::ObjectHeader * object)221 void StwGC<LanguageConfig>::InitGCBits(panda::ObjectHeader *object)
222 {
223 if (!marker_.IsReverseMark()) {
224 object->SetUnMarkedForGC();
225 ASSERT(!object->IsMarkedForGC());
226 } else {
227 object->SetMarkedForGC();
228 ASSERT(object->IsMarkedForGC());
229 }
230 LOG_DEBUG_GC << "Init gc bits for object: " << std::hex << object << " bit: " << object->IsMarkedForGC()
231 << " reversed_mark: " << marker_.IsReverseMark();
232 }
233
234 template <class LanguageConfig>
InitGCBitsForAllocationInTLAB(panda::ObjectHeader * obj_header)235 void StwGC<LanguageConfig>::InitGCBitsForAllocationInTLAB([[maybe_unused]] panda::ObjectHeader *obj_header)
236 {
237 LOG(FATAL, GC) << "TLABs are not supported by this GC";
238 }
239
240 template <class LanguageConfig>
Trigger()241 void StwGC<LanguageConfig>::Trigger()
242 {
243 auto task = MakePandaUnique<GCTask>(GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE, time::GetCurrentTimeInNanos());
244 this->AddGCTask(true, std::move(task), true);
245 }
246
247 template <class LanguageConfig>
WorkerTaskProcessing(GCWorkersTask * task,void * worker_data)248 void StwGC<LanguageConfig>::WorkerTaskProcessing(GCWorkersTask *task, [[maybe_unused]] void *worker_data)
249 {
250 switch (task->GetType()) {
251 case GCWorkersTaskTypes::TASK_MARKING: {
252 auto stack = task->GetMarkingStack();
253 MarkStack(stack, []([[maybe_unused]] const ObjectHeader *obj) { return true; });
254 this->GetInternalAllocator()->Delete(stack);
255 break;
256 }
257 default:
258 LOG(FATAL, GC) << "Unimplemented for " << GCWorkersTaskTypesToString(task->GetType());
259 UNREACHABLE();
260 }
261 }
262
263 template <class LanguageConfig>
MarkObject(ObjectHeader * object)264 void StwGC<LanguageConfig>::MarkObject(ObjectHeader *object)
265 {
266 marker_.Mark(object);
267 }
268
269 template <class LanguageConfig>
UnMarkObject(ObjectHeader * object_header)270 void StwGC<LanguageConfig>::UnMarkObject([[maybe_unused]] ObjectHeader *object_header)
271 {
272 LOG(FATAL, GC) << "UnMarkObject for STW GC shouldn't be called";
273 }
274
275 template <class LanguageConfig>
MarkReferences(GCMarkingStackType * references,GCPhase gc_phase)276 void StwGC<LanguageConfig>::MarkReferences(GCMarkingStackType *references, [[maybe_unused]] GCPhase gc_phase)
277 {
278 trace::ScopedTrace scoped_trace(__FUNCTION__);
279 ASSERT(gc_phase == GCPhase::GC_PHASE_MARK);
280 LOG_DEBUG_GC << "Start marking " << references->Size() << " references";
281 MarkStack(references, []([[maybe_unused]] const ObjectHeader *obj) { return true; });
282 }
283
284 template <class LanguageConfig>
IsMarked(const ObjectHeader * object) const285 bool StwGC<LanguageConfig>::IsMarked(const ObjectHeader *object) const
286 {
287 return marker_.IsMarked(object);
288 }
289
290 TEMPLATE_CLASS_LANGUAGE_CONFIG(StwGC);
291 template class StwGCMarker<LANG_TYPE_STATIC, false>;
292 template class StwGCMarker<LANG_TYPE_DYNAMIC, false>;
293
294 } // namespace panda::mem
295