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/generational-gc-base.h"
17 #include "runtime/include/runtime.h"
18 #include "runtime/include/runtime_notification.h"
19 #include "runtime/include/panda_vm.h"
20
21 namespace panda::mem {
22
23 template <class LanguageConfig>
ShouldRunTenuredGC(const GCTask & task)24 bool GenerationalGC<LanguageConfig>::ShouldRunTenuredGC(const GCTask &task)
25 {
26 return this->IsOnPygoteFork() || task.reason_ == GCTaskCause::OOM_CAUSE ||
27 task.reason_ == GCTaskCause::EXPLICIT_CAUSE || task.reason_ == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE ||
28 task.reason_ == GCTaskCause::STARTUP_COMPLETE_CAUSE;
29 }
30
31 template <class LanguageConfig>
WaitForGC(GCTask task)32 void GenerationalGC<LanguageConfig>::WaitForGC(GCTask task)
33 {
34 // TODO(maksenov): Notify only about pauses (#4681)
35 Runtime::GetCurrent()->GetNotificationManager()->GarbageCollectorStartEvent();
36 // Atomic with acquire order reason: data race with gc_counter_ with dependecies on reads after the load which
37 // should become visible
38 auto old_counter = this->gc_counter_.load(std::memory_order_acquire);
39 Timing suspend_threads_timing;
40 {
41 ScopedTiming t("SuspendThreads", suspend_threads_timing);
42 this->GetPandaVm()->GetRendezvous()->SafepointBegin();
43 }
44
45 // Atomic with acquire order reason: data race with gc_counter_ with dependecies on reads after the load which
46 // should become visible
47 auto new_counter = this->gc_counter_.load(std::memory_order_acquire);
48 // Atomic with acquire order reason: data race with last_cause_ with dependecies on reads after the load which
49 // should become visible
50 if (new_counter > old_counter && this->last_cause_.load(std::memory_order_acquire) >= task.reason_) {
51 this->GetPandaVm()->GetRendezvous()->SafepointEnd();
52 return;
53 }
54
55 // Create a copy of the constant GCTask to be able to change its value
56 this->RunPhases(task);
57
58 if (UNLIKELY(this->IsLogDetailedGcInfoEnabled())) {
59 LOG(INFO, GC) << mem_stats_.Dump();
60 for (auto &footprint : this->footprint_list_) {
61 LOG(INFO, GC) << footprint.first << " : " << footprint.second;
62 }
63 LOG(INFO, GC) << suspend_threads_timing.Dump();
64 LOG(INFO, GC) << this->GetTiming()->Dump();
65 }
66 this->GetTiming()->Reset(); // Clear records.
67
68 this->GetPandaVm()->GetRendezvous()->SafepointEnd();
69 Runtime::GetCurrent()->GetNotificationManager()->GarbageCollectorFinishEvent();
70 this->GetPandaVm()->HandleGCFinished();
71 this->GetPandaVm()->HandleEnqueueReferences();
72 }
73
74 template <class LanguageConfig>
Dump()75 PandaString GenerationalGC<LanguageConfig>::MemStats::Dump()
76 {
77 PandaStringStream statistic;
78 statistic << "Young freed " << young_free_object_count_ << " objects ("
79 << helpers::MemoryConverter(young_free_object_size_) << ") Young moved " << young_move_object_count_
80 << " objects, " << young_move_object_size_ << " bytes ("
81 << helpers::MemoryConverter(young_move_object_size_) << ")";
82 if (tenured_free_object_size_ > 0U) {
83 statistic << " Tenured freed " << tenured_free_object_size_ << "("
84 << helpers::MemoryConverter(tenured_free_object_size_) << ")";
85 }
86 return statistic.str();
87 }
88
89 template <class LanguageConfig>
UpdateMemStats(size_t bytes_in_heap_before,bool update_tenured_stats,bool record_allocation_for_moved_objects)90 void GenerationalGC<LanguageConfig>::UpdateMemStats(size_t bytes_in_heap_before, bool update_tenured_stats,
91 bool record_allocation_for_moved_objects)
92 {
93 size_t young_move_size = this->mem_stats_.GetSizeMovedYoung();
94 size_t young_move_count = this->mem_stats_.GetCountMovedYoung();
95 size_t young_delete_size = this->mem_stats_.GetSizeFreedYoung();
96 size_t young_delete_count = this->mem_stats_.GetCountFreedYoung();
97 size_t tenured_move_size = update_tenured_stats ? this->mem_stats_.GetSizeMovedTenured() : 0;
98 size_t tenured_move_count = update_tenured_stats ? this->mem_stats_.GetCountMovedTenured() : 0;
99 size_t tenured_delete_size = update_tenured_stats ? this->mem_stats_.GetSizeFreedTenured() : 0;
100 size_t tenured_delete_count = update_tenured_stats ? this->mem_stats_.GetCountFreedTenured() : 0;
101
102 auto *vm_mem_stats = this->GetPandaVm()->GetMemStats();
103 GCInstanceStats *gc_stats = this->GetStats();
104
105 if (record_allocation_for_moved_objects) {
106 vm_mem_stats->RecordAllocateObjects(young_move_count + tenured_move_count, young_move_size + tenured_move_size,
107 SpaceType::SPACE_TYPE_OBJECT);
108 }
109 if (young_move_size > 0) {
110 gc_stats->AddMemoryValue(young_move_size, MemoryTypeStats::MOVED_BYTES);
111 gc_stats->AddObjectsValue(young_move_count, ObjectTypeStats::MOVED_OBJECTS);
112 vm_mem_stats->RecordYoungMovedObjects(young_move_count, young_move_size, SpaceType::SPACE_TYPE_OBJECT);
113 }
114 if (young_delete_size > 0) {
115 gc_stats->AddMemoryValue(young_delete_size, MemoryTypeStats::YOUNG_FREED_BYTES);
116 gc_stats->AddObjectsValue(young_delete_count, ObjectTypeStats::YOUNG_FREED_OBJECTS);
117 }
118
119 if (bytes_in_heap_before > 0) {
120 gc_stats->AddCopiedRatioValue(static_cast<double>(young_move_size + tenured_move_size) / bytes_in_heap_before);
121 }
122
123 if (tenured_move_size > 0) {
124 gc_stats->AddMemoryValue(tenured_move_size, MemoryTypeStats::MOVED_BYTES);
125 gc_stats->AddObjectsValue(tenured_move_count, ObjectTypeStats::MOVED_OBJECTS);
126 vm_mem_stats->RecordTenuredMovedObjects(tenured_move_count, tenured_move_size, SpaceType::SPACE_TYPE_OBJECT);
127 }
128 if (tenured_delete_size > 0) {
129 gc_stats->AddMemoryValue(tenured_delete_size, MemoryTypeStats::ALL_FREED_BYTES);
130 gc_stats->AddObjectsValue(tenured_delete_count, ObjectTypeStats::ALL_FREED_OBJECTS);
131 }
132 vm_mem_stats->RecordFreeObjects(young_delete_count + tenured_delete_count, young_delete_size + tenured_delete_size,
133 SpaceType::SPACE_TYPE_OBJECT);
134 }
135
136 template <class LanguageConfig>
SweepStringTableYoung(const std::function<bool (ObjectHeader *)> & young_checker)137 void GenerationalGC<LanguageConfig>::SweepStringTableYoung(const std::function<bool(ObjectHeader *)> &young_checker)
138 {
139 GCScope<TRACE_TIMING_PHASE> scoped_trace(__FUNCTION__, this, GCPhase::GC_PHASE_SWEEP_STRING_TABLE_YOUNG);
140
141 this->GetPandaVm()->SweepStringTable(static_cast<GCObjectVisitor>([&young_checker](ObjectHeader *object_header) {
142 if (young_checker(object_header)) {
143 return ObjectStatus::DEAD_OBJECT;
144 }
145 return ObjectStatus::ALIVE_OBJECT;
146 }));
147 }
148
149 template <class LanguageConfig>
CreateCardTable(InternalAllocatorPtr internal_allocator_ptr,uintptr_t min_address,size_t size)150 void GenerationalGC<LanguageConfig>::CreateCardTable(InternalAllocatorPtr internal_allocator_ptr, uintptr_t min_address,
151 size_t size)
152 {
153 card_table_ = MakePandaUnique<CardTable>(internal_allocator_ptr, min_address, size);
154 card_table_->Initialize();
155 }
156
157 TEMPLATE_CLASS_LANGUAGE_CONFIG(GenerationalGC);
158 } // namespace panda::mem
159