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