• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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