1 /*
2 * Copyright (c) 2025 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 "common_components/heap/allocator/region_manager.h"
17
18 #include <algorithm>
19 #include <cmath>
20 #include <cstdint>
21 #include <unistd.h>
22
23 #include "common_components/common_runtime/hooks.h"
24 #include "common_components/heap/allocator/region_desc.h"
25 #include "common_components/heap/allocator/region_list.h"
26 #include "common_components/heap/allocator/region_space.h"
27 #include "common_components/base/c_string.h"
28 #include "common_components/heap/collector/collector.h"
29 #include "common_components/heap/collector/marking_collector.h"
30 #include "common_components/common/base_object.h"
31 #include "common_components/common/scoped_object_access.h"
32 #include "common_components/heap/heap.h"
33 #include "common_components/mutator/mutator.inline.h"
34 #include "common_components/mutator/mutator_manager.h"
35 #include "common_components/heap/allocator/fix_heap.h"
36
37 #if defined(COMMON_TSAN_SUPPORT)
38 #include "common_components/sanitizer/sanitizer_interface.h"
39 #endif
40 #include "common_components/log/log.h"
41 #include "common_components/taskpool/taskpool.h"
42 #include "common_interfaces/base_runtime.h"
43
44 #if defined(_WIN64)
45 #include <sysinfoapi.h>
46 #endif
47
48 namespace common {
49 uintptr_t RegionDesc::UnitInfo::totalUnitCount = 0;
50 uintptr_t RegionDesc::UnitInfo::unitInfoStart = 0;
51 uintptr_t RegionDesc::UnitInfo::heapStartAddress = 0;
52
GetPageSize()53 static size_t GetPageSize() noexcept
54 {
55 #if defined(_WIN64)
56 SYSTEM_INFO systeminfo;
57 GetSystemInfo(&systeminfo);
58 if (systeminfo.dwPageSize != 0) {
59 return systeminfo.dwPageSize;
60 } else {
61 // default page size is 4KB if get system page size failed.
62 return 4 * KB;
63 }
64 #elif defined(__APPLE__)
65 // default page size is 4KB in MacOS
66 return 4 * KB;
67 #else
68 return getpagesize();
69 #endif
70 }
71
72 // System default page size
73 const size_t COMMON_PAGE_SIZE = GetPageSize();
74 const size_t AllocatorUtils::ALLOC_PAGE_SIZE = COMMON_PAGE_SIZE;
75 // size of huge page is 2048KB.
76 constexpr size_t HUGE_PAGE_UNIT_NUM = (2048 * KB) / RegionDesc::UNIT_SIZE;
77
78 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG
DumpRegionDesc(LogType type) const79 void RegionDesc::DumpRegionDesc(LogType type) const
80 {
81 DLOG(type, "Region index: %zu, type: %s, address: 0x%zx(start=0x%zx)-0x%zx, allocated(B) %zu, live(B) %zu",
82 GetUnitIdx(), GetTypeName(), GetRegionBase(), GetRegionStart(), GetRegionEnd(), GetRegionAllocatedSize(),
83 GetLiveByteCount());
84 }
85
GetTypeName() const86 const char* RegionDesc::GetTypeName() const
87 {
88 static constexpr const char* regionNames[] = {
89 "undefined region",
90 "thread local region",
91 "recent fullregion",
92 "from region",
93 "unmovable from region",
94 "to region",
95 "full pinned region",
96 "recent pinned region",
97 "raw pointer pinned region",
98 "tl raw pointer region",
99 "large region",
100 "recent large region",
101 "garbage region",
102 };
103 return regionNames[static_cast<uint8_t>(GetRegionType())];
104 }
105 #endif
106
VisitAllObjects(const std::function<void (BaseObject *)> && func)107 void RegionDesc::VisitAllObjects(const std::function<void(BaseObject*)>&& func)
108 {
109 uintptr_t allocPtr = GetRegionAllocPtr();
110 VisitAllObjectsBefore(std::move(func), GetRegionAllocPtr());
111 }
112
VisitAllObjectsBefore(const std::function<void (BaseObject *)> && func,uintptr_t end)113 void RegionDesc::VisitAllObjectsBefore(const std::function<void(BaseObject *)> &&func, uintptr_t end)
114 {
115 uintptr_t position = GetRegionStart();
116
117 if (IsFixedRegion()) {
118 size_t size = static_cast<size_t>(GetRegionCellCount() + 1) * sizeof(uint64_t);
119 while (position < end) {
120 BaseObject *obj = reinterpret_cast<BaseObject *>(position);
121 position += size;
122 if (position > end) {
123 break;
124 }
125 if (IsFreePinnedObject(obj)) {
126 continue;
127 }
128 func(obj);
129 }
130 return;
131 } else if (IsLargeRegion() && (position < end)) {
132 if (IsJitFortAwaitInstallFlag()) {
133 return;
134 }
135 func(reinterpret_cast<BaseObject *>(GetRegionStart()));
136 } else if (IsSmallRegion()) {
137 while (position < end) {
138 // GetAllocSize should before call func, because object maybe destroy in compact gc.
139 func(reinterpret_cast<BaseObject *>(position));
140 size_t size = RegionSpace::GetAllocSize(*reinterpret_cast<BaseObject *>(position));
141 position += size;
142 }
143 }
144 }
145
VisitAllObjectsWithFixedSize(size_t cellCount,const std::function<void (BaseObject *)> && func)146 void RegionDesc::VisitAllObjectsWithFixedSize(size_t cellCount, const std::function<void(BaseObject*)>&& func)
147 {
148 CHECK_CC(GetRegionType() == RegionType::FIXED_PINNED_REGION ||
149 GetRegionType() == RegionType::FULL_FIXED_PINNED_REGION);
150 size_t size = (cellCount + 1) * sizeof(uint64_t);
151 uintptr_t position = GetRegionStart();
152 uintptr_t end = GetRegionAllocPtr();
153 while (position < end) {
154 // GetAllocSize should before call func, because object maybe destroy in compact gc.
155 BaseObject* obj = reinterpret_cast<BaseObject*>(position);
156 position += size;
157 if (position > end) {
158 break;
159 }
160 func(obj);
161 }
162 }
163
VisitAllObjectsBeforeCopy(const std::function<void (BaseObject *)> && func)164 void RegionDesc::VisitAllObjectsBeforeCopy(const std::function<void(BaseObject*)>&& func)
165 {
166 uintptr_t allocPtr = GetRegionAllocPtr();
167 uintptr_t phaseLine = GetCopyLine();
168 uintptr_t end = std::min(phaseLine, allocPtr);
169 VisitAllObjectsBefore(std::move(func), end);
170 }
171
VisitLiveObjectsUntilFalse(const std::function<bool (BaseObject *)> && func)172 bool RegionDesc::VisitLiveObjectsUntilFalse(const std::function<bool(BaseObject*)>&& func)
173 {
174 // no need to visit this region.
175 if (GetLiveByteCount() == 0) {
176 return true;
177 }
178
179 MarkingCollector& collector = reinterpret_cast<MarkingCollector&>(Heap::GetHeap().GetCollector());
180 if (IsLargeRegion()) {
181 if (IsJitFortAwaitInstallFlag()) {
182 return true;
183 }
184 return func(reinterpret_cast<BaseObject *>(GetRegionStart()));
185 }
186 if (IsSmallRegion()) {
187 uintptr_t position = GetRegionStart();
188 uintptr_t allocPtr = GetRegionAllocPtr();
189 while (position < allocPtr) {
190 BaseObject* obj = reinterpret_cast<BaseObject*>(position);
191 if (collector.IsSurvivedObject(obj) && !func(obj)) { return false; }
192 position += RegionSpace::GetAllocSize(*obj);
193 }
194 }
195 return true;
196 }
197
VisitRememberSetBeforeMarking(const std::function<void (BaseObject *)> & func)198 void RegionDesc::VisitRememberSetBeforeMarking(const std::function<void(BaseObject*)>& func)
199 {
200 if (IsLargeRegion() && IsJitFortAwaitInstallFlag()) {
201 // machine code which is not installed should skip here.
202 return;
203 }
204 uintptr_t end = std::min(GetMarkingLine(), GetRegionAllocPtr());
205 GetRSet()->VisitAllMarkedCardBefore(func, GetRegionBaseFast(), end);
206 }
207
VisitRememberSetBeforeCopy(const std::function<void (BaseObject *)> & func)208 void RegionDesc::VisitRememberSetBeforeCopy(const std::function<void(BaseObject*)>& func)
209 {
210 if (IsLargeRegion() && IsJitFortAwaitInstallFlag()) {
211 // machine code which is not installed should skip here.
212 return;
213 }
214 uintptr_t end = std::min(GetCopyLine(), GetRegionAllocPtr());
215 GetRSet()->VisitAllMarkedCardBefore(func, GetRegionBaseFast(), end);
216 }
217
VisitRememberSet(const std::function<void (BaseObject *)> & func)218 void RegionDesc::VisitRememberSet(const std::function<void(BaseObject*)>& func)
219 {
220 if (IsLargeRegion() && IsJitFortAwaitInstallFlag()) {
221 // machine code which is not installed should skip here.
222 return;
223 }
224 GetRSet()->VisitAllMarkedCardBefore(func, GetRegionBaseFast(), GetRegionAllocPtr());
225 }
226
MergeRegionListWithoutHead(RegionList & srcList,RegionDesc::RegionType regionType)227 void RegionList::MergeRegionListWithoutHead(RegionList& srcList, RegionDesc::RegionType regionType)
228 {
229 RegionDesc *head = srcList.TakeHeadRegion();
230 MergeRegionList(srcList, regionType);
231 if (head) {
232 srcList.PrependRegion(head, head->GetRegionType());
233 }
234 }
235
MergeRegionList(RegionList & srcList,RegionDesc::RegionType regionType)236 void RegionList::MergeRegionList(RegionList& srcList, RegionDesc::RegionType regionType)
237 {
238 RegionList regionList("region list cache");
239 srcList.MoveTo(regionList);
240 RegionDesc* head = regionList.GetHeadRegion();
241 RegionDesc* tail = regionList.GetTailRegion();
242 if (head == nullptr) {
243 return;
244 }
245 std::lock_guard<std::mutex> lock(listMutex_);
246 regionList.SetElementType(regionType);
247 IncCounts(regionList.GetRegionCount(), regionList.GetUnitCount());
248 if (listHead_ == nullptr) {
249 listHead_ = head;
250 listTail_ = tail;
251 } else {
252 tail->SetNextRegion(listHead_);
253 listHead_->SetPrevRegion(tail);
254 listHead_ = head;
255 }
256 }
257
RegionDescRegionTypeToString(RegionDesc::RegionType type)258 static const char *RegionDescRegionTypeToString(RegionDesc::RegionType type)
259 {
260 static constexpr const char *enumStr[] = {
261 [static_cast<uint8_t>(RegionDesc::RegionType::FREE_REGION)] = "FREE_REGION",
262 [static_cast<uint8_t>(RegionDesc::RegionType::GARBAGE_REGION)] = "GARBAGE_REGION",
263 [static_cast<uint8_t>(RegionDesc::RegionType::THREAD_LOCAL_REGION)] = "THREAD_LOCAL_REGION",
264 [static_cast<uint8_t>(RegionDesc::RegionType::THREAD_LOCAL_OLD_REGION)] = "THREAD_LOCAL_OLD_REGION",
265 [static_cast<uint8_t>(RegionDesc::RegionType::RECENT_FULL_REGION)] = "RECENT_FULL_REGION",
266 [static_cast<uint8_t>(RegionDesc::RegionType::FROM_REGION)] = "FROM_REGION",
267 [static_cast<uint8_t>(RegionDesc::RegionType::LONE_FROM_REGION)] = "LONE_FROM_REGION",
268 [static_cast<uint8_t>(RegionDesc::RegionType::EXEMPTED_FROM_REGION)] = "EXEMPTED_FROM_REGION",
269 [static_cast<uint8_t>(RegionDesc::RegionType::TO_REGION)] = "TO_REGION",
270 [static_cast<uint8_t>(RegionDesc::RegionType::OLD_REGION)] = "OLD_REGION",
271 [static_cast<uint8_t>(RegionDesc::RegionType::FULL_PINNED_REGION)] = "FULL_PINNED_REGION",
272 [static_cast<uint8_t>(RegionDesc::RegionType::RECENT_PINNED_REGION)] = "RECENT_PINNED_REGION",
273 [static_cast<uint8_t>(RegionDesc::RegionType::FIXED_PINNED_REGION)] = "FIXED_PINNED_REGION",
274 [static_cast<uint8_t>(RegionDesc::RegionType::FULL_FIXED_PINNED_REGION)] = "FULL_FIXED_PINNED_REGION",
275 [static_cast<uint8_t>(RegionDesc::RegionType::RAW_POINTER_REGION)] = "RAW_POINTER_REGION",
276 [static_cast<uint8_t>(RegionDesc::RegionType::TL_RAW_POINTER_REGION)] = "TL_RAW_POINTER_REGION",
277 [static_cast<uint8_t>(RegionDesc::RegionType::RECENT_LARGE_REGION)] = "RECENT_LARGE_REGION",
278 [static_cast<uint8_t>(RegionDesc::RegionType::LARGE_REGION)] = "LARGE_REGION",
279 [static_cast<uint8_t>(RegionDesc::RegionType::READ_ONLY_REGION)] = "READ_ONLY_REGION",
280 [static_cast<uint8_t>(RegionDesc::RegionType::APPSPAWN_REGION)] = "APPSPAWN_REGION",
281 };
282 ASSERT_LOGF(type < RegionDesc::RegionType::END_OF_REGION_TYPE, "Invalid region type");
283 return enumStr[static_cast<uint8_t>(type)];
284 }
285
PrependRegion(RegionDesc * region,RegionDesc::RegionType type)286 void RegionList::PrependRegion(RegionDesc* region, RegionDesc::RegionType type)
287 {
288 std::lock_guard<std::mutex> lock(listMutex_);
289 PrependRegionLocked(region, type);
290 }
291
PrependRegionLocked(RegionDesc * region,RegionDesc::RegionType type)292 void RegionList::PrependRegionLocked(RegionDesc* region, RegionDesc::RegionType type)
293 {
294 if (region == nullptr) {
295 return;
296 }
297
298 DLOG(REGION, "%s (%zu, %zu)+(%zu, %zu) prepend region %p(base=%#zx)@%#zx+%zu type %u->%u", listName_,
299 regionCount_, unitCount_, 1llu, region->GetUnitCount(), region, region->GetRegionBase(),
300 region->GetRegionStart(), region->GetRegionAllocatedSize(), region->GetRegionType(), type);
301
302 region->SetRegionType(type);
303
304 size_t totalRegionSize = region->GetRegionEnd() - region->GetRegionBase();
305 os::PrctlSetVMA(reinterpret_cast<void *>(region->GetRegionBase()), totalRegionSize,
306 (std::string("ArkTS Heap CMCGC Region ") + RegionDescRegionTypeToString(type)).c_str());
307
308 region->SetPrevRegion(nullptr);
309 IncCounts(1, region->GetUnitCount());
310 region->SetNextRegion(listHead_);
311 if (listHead_ == nullptr) {
312 ASSERT_LOGF(listTail_ == nullptr, "PrependRegion listTail is not null");
313 listTail_ = region;
314 } else {
315 listHead_->SetPrevRegion(region);
316 }
317 listHead_ = region;
318 }
319
DeleteRegionLocked(RegionDesc * del)320 void RegionList::DeleteRegionLocked(RegionDesc* del)
321 {
322 ASSERT_LOGF(listHead_ != nullptr && listTail_ != nullptr, "illegal region list");
323
324 RegionDesc* pre = del->GetPrevRegion();
325 RegionDesc* next = del->GetNextRegion();
326
327 del->SetNextRegion(nullptr);
328 del->SetPrevRegion(nullptr);
329
330 DLOG(REGION, "%s (%zu, %zu)-(%zu, %zu) delete region %p(start=%p),@%#zx+%zu type %u", listName_,
331 regionCount_, unitCount_, 1llu, del->GetUnitCount(),
332 del, del->GetRegionBase(), del->GetRegionStart(), del->GetRegionAllocatedSize(), del->GetRegionType());
333
334 DecCounts(1, del->GetUnitCount());
335
336 if (listHead_ == del) { // delete head
337 ASSERT_LOGF(pre == nullptr, "Delete Region pre is not null");
338 listHead_ = next;
339 if (listHead_ == nullptr) { // now empty
340 listTail_ = nullptr;
341 return;
342 }
343 } else {
344 pre->SetNextRegion(next);
345 }
346
347 if (listTail_ == del) { // delete tail
348 ASSERT_LOGF(next == nullptr, "Delete Region next is not null");
349 listTail_ = pre;
350 if (listTail_ == nullptr) { // now empty
351 listHead_ = nullptr;
352 return;
353 }
354 } else {
355 next->SetPrevRegion(pre);
356 }
357 }
358
DumpRegionSummary() const359 void RegionList::DumpRegionSummary() const
360 {
361 VLOG(DEBUG, "\t%s %zu: %zu units (%zu B, alloc %zu)", listName_,
362 regionCount_, unitCount_, GetAllocatedSize(true), GetAllocatedSize(false));
363 }
364
365 #ifndef NDEBUG
DumpRegionList(const char * msg)366 void RegionList::DumpRegionList(const char* msg)
367 {
368 DLOG(REGION, "dump region list %s", msg);
369 std::lock_guard<std::mutex> lock(listMutex_);
370 for (RegionDesc *region = listHead_; region != nullptr; region = region->GetNextRegion()) {
371 DLOG(REGION, "region %p @0x%zx(start=0x%zx)+%zu units [%zu+%zu, %zu) type %u prev %p next %p", region,
372 region->GetRegionBase(), region->GetRegionStart(), region->GetRegionAllocatedSize(),
373 region->GetUnitIdx(), region->GetUnitCount(), region->GetUnitIdx() + region->GetUnitCount(),
374 region->GetRegionType(), region->GetPrevRegion(), region->GetNextRegion());
375 }
376 }
377 #endif
TagHugePage(RegionDesc * region,size_t num) const378 inline void RegionManager::TagHugePage(RegionDesc* region, size_t num) const
379 {
380 #if defined (__linux__) || defined(PANDA_TARGET_OHOS)
381 (void)madvise(reinterpret_cast<void*>(region->GetRegionBase()), num * RegionDesc::UNIT_SIZE, MADV_HUGEPAGE);
382 #else
383 (void)region;
384 (void)num;
385 #endif
386 }
387
UntagHugePage(RegionDesc * region,size_t num) const388 inline void RegionManager::UntagHugePage(RegionDesc* region, size_t num) const
389 {
390 #if defined (__linux__) || defined(PANDA_TARGET_OHOS)
391 (void)madvise(reinterpret_cast<void*>(region->GetRegionBase()), num * RegionDesc::UNIT_SIZE, MADV_NOHUGEPAGE);
392 #else
393 (void)region;
394 (void)num;
395 #endif
396 }
397
ReleaseGarbageRegions(size_t targetCachedSize)398 size_t FreeRegionManager::ReleaseGarbageRegions(size_t targetCachedSize)
399 {
400 size_t dirtyBytes = dirtyUnitTree_.GetTotalCount() * RegionDesc::UNIT_SIZE;
401 if (dirtyBytes <= targetCachedSize) {
402 VLOG(DEBUG, "release heap garbage memory 0 bytes, cache %zu(%zu) bytes", dirtyBytes, targetCachedSize);
403 return 0;
404 }
405
406 size_t releasedBytes = 0;
407 while (dirtyBytes > targetCachedSize) {
408 std::lock_guard<std::mutex> lock1(dirtyUnitTreeMutex_);
409 auto node = dirtyUnitTree_.RootNode();
410 if (node == nullptr) { break; }
411 uint32_t idx = node->GetIndex();
412 uint32_t num = node->GetCount();
413 dirtyUnitTree_.ReleaseRootNode();
414
415 std::lock_guard<std::mutex> lock2(releasedUnitTreeMutex_);
416 LOGF_CHECK(releasedUnitTree_.MergeInsert(idx, num, true)) <<
417 "tid " << GetTid() << ": failed to release garbage units[" <<
418 idx << "+" << num << ", " << (idx + num) << ")";
419 releasedBytes += (num * RegionDesc::UNIT_SIZE);
420 dirtyBytes = dirtyUnitTree_.GetTotalCount() * RegionDesc::UNIT_SIZE;
421 }
422 VLOG(DEBUG, "release heap garbage memory %zu bytes, cache %zu(%zu) bytes",
423 releasedBytes, dirtyBytes, targetCachedSize);
424 return releasedBytes;
425 }
426
Initialize(size_t nRegion,uintptr_t regionInfoAddr)427 void RegionManager::Initialize(size_t nRegion, uintptr_t regionInfoAddr)
428 {
429 size_t metadataSize = GetMetadataSize(nRegion);
430 size_t alignedHeapStart = RoundUp<size_t>(regionInfoAddr + metadataSize, RegionDesc::UNIT_SIZE);
431 // align the start of region to 256KB
432 /***
433 * |***********|<-metadataSize->|**********************|
434 * |**padding**|***RegionUnit***|*******Region*********|
435 * ^ ^ ^
436 * | | |
437 * | reginInfoStart alignedHeapStart
438 * regionInfoAddr
439 */
440 regionInfoStart_ = alignedHeapStart - metadataSize;
441 regionHeapStart_ = alignedHeapStart;
442 #ifdef _WIN64
443 MemoryMap::CommitMemory(reinterpret_cast<void*>(regionInfoStart_), metadataSize);
444 #endif
445 regionHeapEnd_ = regionHeapStart_ + nRegion * RegionDesc::UNIT_SIZE;
446 inactiveZone_ = regionHeapStart_;
447 // propagate region heap layout
448 RegionDesc::Initialize(nRegion, regionInfoStart_, regionHeapStart_);
449 freeRegionManager_.Initialize(nRegion);
450
451 DLOG(REPORT, "region info @0x%zx+%zu, heap [0x%zx, 0x%zx), unit count %zu", regionInfoAddr, metadataSize,
452 regionHeapStart_, regionHeapEnd_, nRegion);
453 #ifdef USE_HWASAN
454 ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<const volatile void *>(regionInfoAddr),
455 metadataSize + nRegion * RegionDesc::UNIT_SIZE);
456 const uintptr_t p_addr = regionInfoAddr;
457 const uintptr_t p_size = metadataSize + nRegion * RegionDesc::UNIT_SIZE;
458 LOG_COMMON(DEBUG) << std::hex << "set [" << p_addr <<
459 std::hex << ", " << p_addr + p_size << ") unpoisoned\n";
460 #endif
461 }
462
ReclaimRegion(RegionDesc * region)463 void RegionManager::ReclaimRegion(RegionDesc* region)
464 {
465 size_t num = region->GetUnitCount();
466 size_t unitIndex = region->GetUnitIdx();
467 if (num >= HUGE_PAGE_UNIT_NUM) {
468 UntagHugePage(region, num);
469 }
470 DLOG(REGION, "reclaim region %p(base=%#zx)@%#zx+%zu type %u", region,
471 region->GetRegionBase(), region->GetRegionStart(), region->GetRegionAllocatedSize(),
472 region->GetRegionType());
473
474 region->InitFreeUnits();
475 freeRegionManager_.AddGarbageUnits(unitIndex, num);
476 }
477
ReleaseRegion(RegionDesc * region)478 size_t RegionManager::ReleaseRegion(RegionDesc* region)
479 {
480 size_t res = region->GetRegionSize();
481 size_t num = region->GetUnitCount();
482 size_t unitIndex = region->GetUnitIdx();
483 if (num >= HUGE_PAGE_UNIT_NUM) {
484 UntagHugePage(region, num);
485 }
486 DLOG(REGION, "release region %p @%#zx+%zu type %u", region, region->GetRegionStart(),
487 region->GetRegionAllocatedSize(), region->GetRegionType());
488
489 region->InitFreeUnits();
490 RegionDesc::ReleaseUnits(unitIndex, num);
491 freeRegionManager_.AddReleaseUnits(unitIndex, num);
492 return res;
493 }
494
CountLiveObject(const BaseObject * obj)495 void RegionManager::CountLiveObject(const BaseObject* obj)
496 {
497 RegionDesc* region = RegionDesc::GetRegionDescAt(reinterpret_cast<HeapAddress>(obj));
498 region->AddLiveByteCount(obj->GetSize());
499 }
500
AssembleLargeGarbageCandidates()501 void RegionManager::AssembleLargeGarbageCandidates()
502 {
503 largeRegionList_.MergeRegionList(recentLargeRegionList_, RegionDesc::RegionType::LARGE_REGION);
504 }
505
AssemblePinnedGarbageCandidates()506 void RegionManager::AssemblePinnedGarbageCandidates()
507 {
508 pinnedRegionList_.MergeRegionListWithoutHead(recentPinnedRegionList_, RegionDesc::RegionType::FULL_PINNED_REGION);
509 RegionDesc* region = pinnedRegionList_.GetHeadRegion();
510 for (size_t i = 0; i < FIXED_PINNED_REGION_COUNT; i++) {
511 fixedPinnedRegionList_[i]->MergeRegionListWithoutHead(*recentFixedPinnedRegionList_[i],
512 RegionDesc::RegionType::FULL_FIXED_PINNED_REGION);
513 }
514 }
515
ClearRSet()516 void RegionManager::ClearRSet()
517 {
518 auto clearFunc = [](RegionDesc* region) {
519 region->ClearRSet();
520 };
521 recentPinnedRegionList_.VisitAllRegions(clearFunc);
522 pinnedRegionList_.VisitAllRegions(clearFunc);
523 recentLargeRegionList_.VisitAllRegions(clearFunc);
524 largeRegionList_.VisitAllRegions(clearFunc);
525 rawPointerRegionList_.VisitAllRegions(clearFunc);
526 appSpawnRegionList_.VisitAllRegions(clearFunc);
527 for (size_t i = 0; i < FIXED_PINNED_REGION_COUNT; i++) {
528 recentFixedPinnedRegionList_[i]->VisitAllRegions(clearFunc);
529 fixedPinnedRegionList_[i]->VisitAllRegions(clearFunc);
530 }
531 }
532
ForEachObjectUnsafe(const std::function<void (BaseObject *)> & visitor) const533 void RegionManager::ForEachObjectUnsafe(const std::function<void(BaseObject*)>& visitor) const
534 {
535 for (uintptr_t regionAddr = regionHeapStart_; regionAddr < inactiveZone_;) {
536 RegionDesc* region = RegionDesc::GetRegionDescAt(regionAddr);
537 uintptr_t next = region->GetRegionEnd();
538 regionAddr = next;
539 if (!region->IsValidRegion() || region->IsFreeRegion() || region -> IsGarbageRegion()) {
540 continue;
541 }
542 region->VisitAllObjects([&visitor](BaseObject* object) { visitor(object); });
543 }
544 }
545
ForEachObjectSafe(const std::function<void (BaseObject *)> & visitor) const546 void RegionManager::ForEachObjectSafe(const std::function<void(BaseObject*)>& visitor) const
547 {
548 ScopedEnterSaferegion enterSaferegion(false);
549 STWParam stwParam{"visit-all-objects"};
550 ScopedStopTheWorld stw(stwParam);
551 ForEachObjectUnsafe(visitor);
552 }
553
ForEachAwaitingJitFortUnsafe(const std::function<void (BaseObject *)> & visitor) const554 void RegionManager::ForEachAwaitingJitFortUnsafe(const std::function<void(BaseObject*)>& visitor) const
555 {
556 ASSERT(BaseRuntime::GetInstance()->GetMutatorManager().WorldStopped());
557 for (const auto jitFort : awaitingJitFort_) {
558 visitor(jitFort);
559 }
560 }
561
TakeRegion(size_t num,RegionDesc::UnitRole type,bool expectPhysicalMem,bool allowGC,bool isCopy)562 RegionDesc *RegionManager::TakeRegion(size_t num, RegionDesc::UnitRole type, bool expectPhysicalMem, bool allowGC,
563 bool isCopy)
564 {
565 // a chance to invoke heuristic gc.
566 if (allowGC && !Heap::GetHeap().IsGcStarted()) {
567 Heap::GetHeap().TryHeuristicGC();
568 }
569
570 // check for allocation since we do not want gc threads and mutators do any harm to each other.
571 size_t size = num * RegionDesc::UNIT_SIZE;
572 RequestForRegion(size);
573
574 RegionDesc* head = garbageRegionList_.TakeHeadRegion();
575 if (head != nullptr) {
576 DLOG(REGION, "take garbage region %p@%#zx+%zu", head, head->GetRegionStart(), head->GetRegionSize());
577 if (head->GetUnitCount() == num) {
578 auto idx = head->GetUnitIdx();
579 #ifdef USE_HWASAN
580 const uintptr_t pAddr = RegionDesc::GetUnitAddress(idx);
581 ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<const volatile void *>(pAddr), size);
582 LOG_COMMON(DEBUG) << std::hex << "set [" << pAddr <<
583 std::hex << ", " << pAddr + size << ") unpoisoned\n";
584 #endif
585 RegionDesc::ClearUnits(idx, num);
586 DLOG(REGION, "reuse garbage region %p@%#zx+%zu", head, head->GetRegionStart(), head->GetRegionSize());
587 return RegionDesc::ResetRegion(idx, num, type);
588 } else {
589 DLOG(REGION, "reclaim garbage region %p@%#zx+%zu", head, head->GetRegionStart(), head->GetRegionSize());
590 ReclaimRegion(head);
591 }
592 }
593
594 RegionDesc* region = freeRegionManager_.TakeRegion(num, type, expectPhysicalMem);
595 if (region != nullptr) {
596 if (num >= HUGE_PAGE_UNIT_NUM) {
597 TagHugePage(region, num);
598 }
599 return region;
600 }
601
602 // when free regions are not enough for allocation
603 if (num <= GetInactiveUnitCount()) {
604 uintptr_t addr = inactiveZone_.fetch_add(size);
605
606 #ifndef PANDA_TARGET_32
607 size_t totalHeapSize = regionHeapEnd_ - regionHeapStart_;
608 // 2: half space reserved for forward copy. throw oom when gc finish.
609 if (GetActiveSize() * 2 > totalHeapSize) {
610 if (!isCopy) {
611 (void)inactiveZone_.fetch_sub(size);
612 return nullptr;
613 } else {
614 Heap::GetHeap().SetForceThrowOOM(true);
615 }
616 }
617 #endif
618
619 if (addr < regionHeapEnd_ - size) {
620 region = RegionDesc::InitRegionAt(addr, num, type);
621 size_t idx = region->GetUnitIdx();
622 #ifdef _WIN64
623 MemoryMap::CommitMemory(
624 reinterpret_cast<void*>(RegionDesc::GetUnitAddress(idx)), num * RegionDesc::UNIT_SIZE);
625 #endif
626 (void)idx; // eliminate compilation warning
627 DLOG(REGION, "take inactive units [%zu+%zu, %zu) at [0x%zx, 0x%zx)", idx, num, idx + num,
628 RegionDesc::GetUnitAddress(idx), RegionDesc::GetUnitAddress(idx + num));
629 if (num >= HUGE_PAGE_UNIT_NUM) {
630 TagHugePage(region, num);
631 }
632 if (expectPhysicalMem) {
633 RegionDesc::ClearUnits(idx, num);
634 }
635 return region;
636 } else {
637 (void)inactiveZone_.fetch_sub(size);
638 }
639 }
640
641 return nullptr;
642 }
643
644
FixFixedRegionList(MarkingCollector & collector,RegionList & list,size_t cellCount,GCStats & stats)645 void RegionManager::FixFixedRegionList(MarkingCollector& collector, RegionList& list, size_t cellCount, GCStats& stats)
646 {
647 size_t garbageSize = 0;
648 RegionDesc* region = list.GetHeadRegion();
649 while (region != nullptr) {
650 auto liveBytes = region->GetLiveByteCount();
651 if (liveBytes == 0) {
652 RegionDesc* del = region;
653 region = region->GetNextRegion();
654 list.DeleteRegion(del);
655
656 garbageSize += CollectRegion(del);
657 continue;
658 }
659 region->VisitAllObjectsWithFixedSize(cellCount,
660 [&collector, ®ion, &cellCount, &garbageSize](BaseObject* object) {
661 if (collector.IsSurvivedObject(object)) {
662 collector.FixObjectRefFields(object);
663 } else {
664 DLOG(ALLOC, "reclaim pinned obj %p", object);
665 garbageSize += (cellCount + 1) * sizeof(uint64_t);
666 region->CollectPinnedGarbage(object, cellCount);
667 }
668 });
669 region = region->GetNextRegion();
670 }
671 stats.pinnedGarbageSize += garbageSize;
672 }
673
CollectFixHeapTaskForPinnedRegion(MarkingCollector & collector,RegionList & list,FixHeapTaskList & taskList)674 void RegionManager::CollectFixHeapTaskForPinnedRegion(MarkingCollector &collector, RegionList &list,
675 FixHeapTaskList &taskList)
676 {
677 RegionDesc *region = list.GetHeadRegion();
678 while (region != nullptr) {
679 auto liveBytes = region->GetLiveByteCount();
680 if (liveBytes == 0) {
681 PostFixHeapWorker::AddEmptyRegionToCollectDuringPostFix(&list, region);
682 region = region->GetNextRegion();
683 continue;
684 }
685 taskList.push_back({region, FIX_REGION});
686 region = region->GetNextRegion();
687 }
688 }
689
CollectFixTasks(FixHeapTaskList & taskList)690 void RegionManager::CollectFixTasks(FixHeapTaskList& taskList)
691 {
692 // fix all objects.
693 if (Heap::GetHeap().GetGCReason() == GC_REASON_YOUNG) {
694 FixHeapWorker::CollectFixHeapTasks(taskList, largeRegionList_, FIX_OLD_REGION);
695 FixHeapWorker::CollectFixHeapTasks(taskList, appSpawnRegionList_, FIX_OLD_REGION);
696
697 FixHeapWorker::CollectFixHeapTasks(taskList, recentLargeRegionList_, FIX_RECENT_OLD_REGION);
698 FixHeapWorker::CollectFixHeapTasks(taskList, recentPinnedRegionList_, FIX_RECENT_OLD_REGION);
699 FixHeapWorker::CollectFixHeapTasks(taskList, rawPointerRegionList_, FIX_RECENT_OLD_REGION);
700 FixHeapWorker::CollectFixHeapTasks(taskList, pinnedRegionList_, FIX_OLD_REGION);
701
702 for (size_t i = 0; i < FIXED_PINNED_REGION_COUNT; i++) {
703 FixHeapWorker::CollectFixHeapTasks(taskList, *recentFixedPinnedRegionList_[i], FIX_RECENT_OLD_REGION);
704 FixHeapWorker::CollectFixHeapTasks(taskList, *fixedPinnedRegionList_[i], FIX_OLD_REGION);
705 }
706 } else {
707 // fix only survived objects.
708 FixHeapWorker::CollectFixHeapTasks(taskList, largeRegionList_, FIX_REGION);
709 FixHeapWorker::CollectFixHeapTasks(taskList, appSpawnRegionList_, FIX_REGION);
710
711 // fix survived object but should be with line judgement.
712 FixHeapWorker::CollectFixHeapTasks(taskList, recentLargeRegionList_, FIX_RECENT_REGION);
713 FixHeapWorker::CollectFixHeapTasks(taskList, rawPointerRegionList_, FIX_RECENT_REGION);
714
715 FixHeapWorker::CollectFixHeapTasks(taskList, recentPinnedRegionList_, FIX_RECENT_REGION);
716 MarkingCollector &collector = reinterpret_cast<MarkingCollector &>(Heap::GetHeap().GetCollector());
717 CollectFixHeapTaskForPinnedRegion(collector, pinnedRegionList_, taskList);
718 for (size_t i = 0; i < FIXED_PINNED_REGION_COUNT; i++) {
719 FixHeapWorker::CollectFixHeapTasks(taskList, *recentFixedPinnedRegionList_[i], FIX_RECENT_REGION);
720 CollectFixHeapTaskForPinnedRegion(collector, *fixedPinnedRegionList_[i], taskList);
721 }
722 }
723 }
724
725
CollectLargeGarbage()726 size_t RegionManager::CollectLargeGarbage()
727 {
728 OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::CollectLargeGarbage", "");
729 size_t garbageSize = 0;
730 MarkingCollector& collector = reinterpret_cast<MarkingCollector&>(Heap::GetHeap().GetCollector());
731 RegionDesc* region = largeRegionList_.GetHeadRegion();
732 while (region != nullptr) {
733 HeapAddress addr = region->GetRegionStart();
734 BaseObject *obj = reinterpret_cast<BaseObject *>(addr);
735
736 if (region->IsJitFortAwaitInstallFlag()) {
737 region = region->GetNextRegion();
738 continue;
739 }
740 if (!collector.IsSurvivedObject(obj) && !region->IsNewObjectSinceMarking(obj)) {
741 DLOG(REGION, "reclaim large region %p@0x%zx+%zu type %u", region, region->GetRegionStart(),
742 region->GetRegionAllocatedSize(), region->GetRegionType());
743
744 RegionDesc* del = region;
745 region = region->GetNextRegion();
746 largeRegionList_.DeleteRegion(del);
747 if (IsMachineCodeObject(reinterpret_cast<HeapAddress>(obj))) {
748 JitFortUnProt(del->GetRegionBaseSize(), reinterpret_cast<void*>(del->GetRegionBaseFast()));
749 }
750 if (del->GetRegionSize() > RegionDesc::LARGE_OBJECT_RELEASE_THRESHOLD) {
751 garbageSize += ReleaseRegion(del);
752 } else {
753 garbageSize += CollectRegion(del);
754 }
755 } else {
756 DLOG(REGION, "clear mark-bit for large region %p@0x%zx+%zu type %u", region, region->GetRegionStart(),
757 region->GetRegionAllocatedSize(), region->GetRegionType());
758 region = region->GetNextRegion();
759 }
760 }
761
762 region = recentLargeRegionList_.GetHeadRegion();
763 while (region != nullptr) {
764 region = region->GetNextRegion();
765 }
766
767 return garbageSize;
768 }
769
770 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG
DumpRegionDesc() const771 void RegionManager::DumpRegionDesc() const
772 {
773 if (!ENABLE_LOG(ALLOC)) {
774 return;
775 }
776 for (uintptr_t regionAddr = regionHeapStart; regionAddr < inactiveZone;) {
777 RegionDesc* region = RegionDesc::GetRegionDescAt(regionAddr);
778 regionAddr = region->GetRegionEnd();
779 if (!region->IsFreeRegion()) {
780 region->DumpRegionDesc(ALLOC);
781 }
782 }
783 }
784 #endif
785
DumpRegionStats() const786 void RegionManager::DumpRegionStats() const
787 {
788 size_t totalSize = regionHeapEnd_ - regionHeapStart_;
789 size_t totalUnits = totalSize / RegionDesc::UNIT_SIZE;
790 size_t activeSize = inactiveZone_ - regionHeapStart_;
791 size_t activeUnits = activeSize / RegionDesc::UNIT_SIZE;
792 VLOG(DEBUG, "\ttotal units: %zu (%zu B)", totalUnits, totalSize);
793 VLOG(DEBUG, "\tactive units: %zu (%zu B)", activeUnits, activeSize);
794
795 garbageRegionList_.DumpRegionSummary();
796 pinnedRegionList_.DumpRegionSummary();
797 recentPinnedRegionList_.DumpRegionSummary();
798 rawPointerRegionList_.DumpRegionSummary();
799 largeRegionList_.DumpRegionSummary();
800 recentLargeRegionList_.DumpRegionSummary();
801 readOnlyRegionList_.DumpRegionSummary();
802 appSpawnRegionList_.DumpRegionSummary();
803
804 size_t releasedUnits = freeRegionManager_.GetReleasedUnitCount();
805 size_t dirtyUnits = freeRegionManager_.GetDirtyUnitCount();
806 VLOG(DEBUG, "\treleased units: %zu (%zu B)", releasedUnits, releasedUnits * RegionDesc::UNIT_SIZE);
807 VLOG(DEBUG, "\tdirty units: %zu (%zu B)", dirtyUnits, dirtyUnits * RegionDesc::UNIT_SIZE);
808 }
809
RequestForRegion(size_t size)810 void RegionManager::RequestForRegion(size_t size)
811 {
812 if (IsGcThread()) {
813 // gc thread is always permitted for allocation.
814 return;
815 }
816
817 Heap& heap = Heap::GetHeap();
818 GCStats& gcstats = heap.GetCollector().GetGCStats();
819 size_t allocatedBytes = heap.GetAllocatedSize() - gcstats.liveBytesAfterGC;
820 constexpr double pi = 3.14;
821 size_t availableBytesAfterGC = heap.GetMaxCapacity() - gcstats.liveBytesAfterGC;
822 double heuAllocRate = std::cos((pi / 2.0) * allocatedBytes / availableBytesAfterGC) * gcstats.collectionRate;
823 // for maximum performance, choose the larger one.
824 double allocRate = std::max(
825 static_cast<double>(BaseRuntime::GetInstance()->GetHeapParam().allocationRate) * MB / SECOND_TO_NANO_SECOND,
826 heuAllocRate);
827 ASSERT_LOGF(allocRate > 0.00001, "allocRate is zero"); // If it is less than 0.00001, it is considered as 0
828 size_t waitTime = static_cast<size_t>(size / allocRate);
829 uint64_t now = TimeUtil::NanoSeconds();
830 if (prevRegionAllocTime_ + waitTime <= now) {
831 prevRegionAllocTime_ = TimeUtil::NanoSeconds();
832 return;
833 }
834
835 uint64_t sleepTime = std::min<uint64_t>(BaseRuntime::GetInstance()->GetHeapParam().allocationWaitTime,
836 prevRegionAllocTime_ + waitTime - now);
837 DLOG(ALLOC, "wait %zu ns to alloc %zu(B)", sleepTime, size);
838 std::this_thread::sleep_for(std::chrono::nanoseconds{ sleepTime });
839 prevRegionAllocTime_ = TimeUtil::NanoSeconds();
840 }
841
AllocPinnedFromFreeList(size_t cellCount)842 uintptr_t RegionManager::AllocPinnedFromFreeList(size_t cellCount)
843 {
844 GCPhase mutatorPhase = Mutator::GetMutator()->GetMutatorPhase();
845 // workaround: make sure collector doesn't fix newly allocated incomplete objects
846 if (mutatorPhase == GC_PHASE_MARK || mutatorPhase == GC_PHASE_FIX) {
847 return 0;
848 }
849
850 RegionList* list = fixedPinnedRegionList_[cellCount];
851 std::lock_guard<std::mutex> lock(list->GetListMutex());
852 uintptr_t allocPtr = list->AllocFromFreeListInLock();
853 // For making bitmap comform with live object count, do not mark object repeated.
854 if (allocPtr == 0 || mutatorPhase == GCPhase::GC_PHASE_IDLE) {
855 return allocPtr;
856 }
857
858 // Mark new allocated pinned object.
859 RegionDesc* regionDesc = RegionDesc::GetRegionDescAt(allocPtr);
860 BaseObject* object = reinterpret_cast<BaseObject*>(allocPtr);
861 regionDesc->MarkObject(object);
862 size_t size = (cellCount + 1) * sizeof(uint64_t);
863 regionDesc->AddLiveByteCount(size);
864 return allocPtr;
865 }
866
AllocReadOnly(size_t size,bool allowGC)867 uintptr_t RegionManager::AllocReadOnly(size_t size, bool allowGC)
868 {
869 uintptr_t addr = 0;
870 std::mutex& regionListMutex = readOnlyRegionList_.GetListMutex();
871
872 std::lock_guard<std::mutex> lock(regionListMutex);
873 RegionDesc* headRegion = readOnlyRegionList_.GetHeadRegion();
874 if (headRegion != nullptr) {
875 addr = headRegion->Alloc(size);
876 }
877 if (addr == 0) {
878 RegionDesc* region =
879 TakeRegion(1, RegionDesc::UnitRole::SMALL_SIZED_UNITS, false, allowGC);
880 if (region == nullptr) {
881 return 0;
882 }
883 DLOG(REGION, "alloc read only region @0x%zx+%zu type %u", region->GetRegionStart(),
884 region->GetRegionAllocatedSize(),
885 region->GetRegionType());
886 GCPhase phase = Mutator::GetMutator()->GetMutatorPhase();
887 if (phase == GC_PHASE_ENUM || phase == GC_PHASE_MARK || phase == GC_PHASE_REMARK_SATB ||
888 phase == GC_PHASE_POST_MARK) {
889 region->SetMarkingLine();
890 } else if (phase == GC_PHASE_PRECOPY || phase == GC_PHASE_COPY || phase == GC_PHASE_FIX) {
891 region->SetMarkingLine();
892 region->SetCopyLine();
893 }
894
895 // To make sure the allocedSize are consistent, it must prepend region first then alloc object.
896 readOnlyRegionList_.PrependRegionLocked(region, RegionDesc::RegionType::READ_ONLY_REGION);
897 addr = region->Alloc(size);
898 }
899
900 DLOG(ALLOC, "alloc read only obj 0x%zx(%zu)", addr, size);
901 return addr;
902 }
903
MarkRememberSet(const std::function<void (BaseObject *)> & func)904 void RegionManager::MarkRememberSet(const std::function<void(BaseObject*)>& func)
905 {
906 auto visitFunc = [&func](RegionDesc* region) {
907 region->VisitRememberSetBeforeMarking(func);
908 };
909 recentPinnedRegionList_.VisitAllRegions(visitFunc);
910 pinnedRegionList_.VisitAllRegions(visitFunc);
911 recentLargeRegionList_.VisitAllRegions(visitFunc);
912 largeRegionList_.VisitAllRegions(visitFunc);
913 appSpawnRegionList_.VisitAllRegions(visitFunc);
914 rawPointerRegionList_.VisitAllRegions(visitFunc);
915
916 for (size_t i = 0; i < FIXED_PINNED_REGION_COUNT; i++) {
917 recentFixedPinnedRegionList_[i]->VisitAllRegions(visitFunc);
918 fixedPinnedRegionList_[i]->VisitAllRegions(visitFunc);
919 }
920 }
921 } // namespace common
922