1 /*
2 * Copyright (c) 2024 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 "ecmascript/mem/idle_gc_trigger.h"
17
18 #include <algorithm>
19
20 #include "ecmascript/mem/mem_controller.h"
21 #include "ecmascript/mem/shared_mem_controller.h"
22 #include "ecmascript/mem/concurrent_marker.h"
23 #include "ecmascript/mem/heap-inl.h"
24
25 namespace panda::ecmascript {
NotifyVsyncIdleStart()26 void IdleGCTrigger::NotifyVsyncIdleStart()
27 {
28 TryTriggerIdleLocalOldGC();
29 }
30
NotifyLooperIdleStart([[maybe_unused]] int64_t timestamp, [[maybe_unused]] int idleTime)31 void IdleGCTrigger::NotifyLooperIdleStart([[maybe_unused]] int64_t timestamp, [[maybe_unused]] int idleTime)
32 {
33 LOG_ECMA_IF(optionalLogEnabled_, DEBUG) << "IdleGCTrigger: recv once looper idle time";
34 idleState_.store(true);
35 if (!TryTriggerIdleLocalOldGC()) {
36 TryTriggerIdleSharedOldGC();
37 }
38 }
39
NotifyLooperIdleEnd([[maybe_unused]] int64_t timestamp)40 void IdleGCTrigger::NotifyLooperIdleEnd([[maybe_unused]] int64_t timestamp)
41 {
42 idleState_.store(false);
43 }
44
TryTriggerHandleMarkFinished()45 void IdleGCTrigger::TryTriggerHandleMarkFinished()
46 {
47 // wait sharedGC finish
48 if (!thread_->IsReadyToSharedConcurrentMark()) {
49 return ;
50 }
51 if (heap_->GetJSThread()->IsMarkFinished() && heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark()
52 && !heap_->GetOnSerializeEvent() && !heap_->InSensitiveStatus()) {
53 heap_->SetCanThrowOOMError(false);
54 heap_->GetConcurrentMarker()->HandleMarkingFinished();
55 heap_->SetCanThrowOOMError(true);
56 }
57 }
58
TryTriggerLocalConcurrentMark()59 void IdleGCTrigger::TryTriggerLocalConcurrentMark()
60 {
61 if (heap_->GetConcurrentMarker()->IsEnabled() && heap_->CheckCanTriggerConcurrentMarking()) {
62 heap_->SetFullMarkRequestedState(true);
63 heap_->SetMarkType(MarkType::MARK_FULL);
64 heap_->TriggerConcurrentMarking();
65 }
66 }
67
TryTriggerIdleLocalOldGC()68 bool IdleGCTrigger::TryTriggerIdleLocalOldGC()
69 {
70 if (heap_->GetJSThread()->IsMarkFinished() &&
71 heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
72 PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK);
73 return true;
74 }
75 if (!IsPossiblePostGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK) ||
76 !heap_->CheckCanTriggerConcurrentMarking()) {
77 return true;
78 }
79 if (CheckIdleOrHintOldGC<Heap>(heap_) && ReachIdleLocalOldGCThresholds() && !heap_->NeedStopCollection()) {
80 PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK);
81 return true;
82 }
83 return false;
84 }
85
TryTriggerIdleSharedOldGC()86 bool IdleGCTrigger::TryTriggerIdleSharedOldGC()
87 {
88 if (CheckIdleOrHintOldGC<SharedHeap>(sHeap_) &&
89 ReachIdleSharedGCThresholds() && !sHeap_->NeedStopCollection() &&
90 sHeap_->CheckCanTriggerConcurrentMarking(thread_)) {
91 PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK);
92 return true;
93 }
94 return false;
95 }
96
ReachIdleLocalOldGCThresholds()97 bool IdleGCTrigger::ReachIdleLocalOldGCThresholds()
98 {
99 bool isFullMarking = heap_->IsConcurrentFullMark() && heap_->GetJSThread()->IsMarking();
100 bool isNativeSizeLargeTrigger = isFullMarking ? false : heap_->GlobalNativeSizeLargerThanLimitForIdle();
101 if (isNativeSizeLargeTrigger) {
102 return true;
103 }
104
105 OldSpace *oldSpace = heap_->GetOldSpace();
106 HugeObjectSpace *hugeObjectSpace = heap_->GetHugeObjectSpace();
107 size_t idleSizeLimit = static_cast<size_t>(oldSpace->GetInitialCapacity() *
108 IDLE_SPACE_SIZE_LIMIT_RATE);
109 size_t currentSize = oldSpace->GetHeapObjectSize() + hugeObjectSpace->GetHeapObjectSize();
110 if (currentSize >= idleSizeLimit) {
111 return true;
112 }
113
114 size_t maxCapacity = oldSpace->GetMaximumCapacity() + oldSpace->GetOvershootSize() +
115 oldSpace->GetOutOfMemoryOvershootSize();
116 size_t currentCapacity = oldSpace->GetCommittedSize() + hugeObjectSpace->GetCommittedSize();
117 size_t idleCapacityLimit = static_cast<size_t>(maxCapacity * IDLE_SPACE_SIZE_LIMIT_RATE);
118 if (currentCapacity >= idleCapacityLimit) {
119 return true;
120 }
121
122 size_t oldSpaceAllocLimit = heap_->GetGlobalSpaceAllocLimit() + oldSpace->GetOvershootSize();
123 size_t idleOldSpaceAllocLimit = static_cast<size_t>(oldSpaceAllocLimit * IDLE_SPACE_SIZE_LIMIT_RATE);
124 if (heap_->GetHeapObjectSize() > idleOldSpaceAllocLimit) {
125 return true;
126 }
127 return false;
128 }
129
ReachIdleSharedGCThresholds()130 bool IdleGCTrigger::ReachIdleSharedGCThresholds()
131 {
132 size_t expectSizeLimit = sHeap_->GetOldSpace()->GetInitialCapacity() * IDLE_SPACE_SIZE_LIMIT_RATE;
133 size_t currentOldSize = sHeap_->GetOldSpace()->GetHeapObjectSize();
134 size_t expectGlobalSizeLimit = sHeap_->GetGlobalSpaceAllocLimit() * IDLE_SPACE_SIZE_LIMIT_RATE;
135 return currentOldSize > expectSizeLimit || sHeap_->GetHeapObjectSize() > expectGlobalSizeLimit;
136 }
137
TryPostHandleMarkFinished()138 void IdleGCTrigger::TryPostHandleMarkFinished()
139 {
140 if (IsIdleState()) {
141 PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK);
142 }
143 }
144
PostIdleGCTask(TRIGGER_IDLE_GC_TYPE gcType)145 void IdleGCTrigger::PostIdleGCTask(TRIGGER_IDLE_GC_TYPE gcType)
146 {
147 if (triggerGCTaskCallback_ != nullptr && IsPossiblePostGCTask(gcType) && !heap_->NeedStopCollection()) {
148 std::pair<void*, uint8_t> data(heap_->GetEcmaVM(), static_cast<uint8_t>(gcType));
149 triggerGCTaskCallback_(data);
150 SetPostGCTask(gcType);
151 LOG_GC(INFO) << "IdleGCTrigger: post once " << GetGCTypeName(gcType) << " on idleTime";
152 }
153 LOG_GC(DEBUG) << "IdleGCTrigger: failed to post once " << GetGCTypeName(gcType);
154 }
155
CheckIdleYoungGC() const156 bool IdleGCTrigger::CheckIdleYoungGC() const
157 {
158 auto newSpace = heap_->GetNewSpace();
159 LOG_GC(DEBUG) << "IdleGCTrigger: check young GC semi Space size since gc:"
160 << newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop());
161 return newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop()) > IDLE_MIN_EXPECT_RECLAIM_SIZE;
162 }
163
TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE gcType)164 void IdleGCTrigger::TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE gcType)
165 {
166 LOG_GC(DEBUG) << "IdleGCTrigger: recv once notify of " << GetGCTypeName(gcType);
167 switch (gcType) {
168 case TRIGGER_IDLE_GC_TYPE::FULL_GC:
169 if (CheckIdleOrHintFullGC<Heap>(heap_) && !heap_->NeedStopCollection()) {
170 LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
171 heap_->CollectGarbage(TriggerGCType::FULL_GC, GCReason::IDLE);
172 } else if (CheckIdleYoungGC() && !heap_->NeedStopCollection()) {
173 LOG_GC(INFO) << "IdleGCTrigger: trigger young gc";
174 heap_->CollectGarbage(TriggerGCType::YOUNG_GC, GCReason::IDLE);
175 }
176 break;
177 case TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK:
178 if (CheckIdleOrHintOldGC<SharedHeap>(sHeap_) && sHeap_->CheckCanTriggerConcurrentMarking(thread_)
179 && !sHeap_->NeedStopCollection()) {
180 LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
181 sHeap_->TriggerConcurrentMarking<TriggerGCType::SHARED_GC, GCReason::IDLE>(thread_);
182 }
183 break;
184 case TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC:
185 if (CheckIdleOrHintFullGC<SharedHeap>(sHeap_) && !sHeap_->NeedStopCollection()) {
186 LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
187 sHeap_->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::IDLE>(thread_);
188 }
189 break;
190 case TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK:
191 if (CheckIdleOrHintOldGC<Heap>(heap_) && !heap_->NeedStopCollection()) {
192 LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
193 TryTriggerLocalConcurrentMark();
194 }
195 break;
196 case TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK:
197 if (!heap_->NeedStopCollection()) {
198 LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
199 TryTriggerHandleMarkFinished();
200 }
201 break;
202 default: // LCOV_EXCL_BR_LINE
203 LOG_GC(ERROR) << "IdleGCTrigger: this branch is unreachable";
204 return;
205 }
206 ClearPostGCTask(gcType);
207 }
208 }