1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/allocator/partition_allocator/memory_reclaimer.h" 6 7 #include "base/allocator/partition_allocator/partition_alloc.h" 8 #include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" 9 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" 10 #include "base/allocator/partition_allocator/partition_alloc_check.h" 11 #include "base/allocator/partition_allocator/partition_alloc_config.h" 12 13 #if BUILDFLAG(USE_STARSCAN) 14 #include "base/allocator/partition_allocator/starscan/pcscan.h" 15 #endif 16 17 namespace partition_alloc { 18 19 // static Instance()20MemoryReclaimer* MemoryReclaimer::Instance() { 21 static internal::base::NoDestructor<MemoryReclaimer> instance; 22 return instance.get(); 23 } 24 RegisterPartition(PartitionRoot<> * partition)25void MemoryReclaimer::RegisterPartition(PartitionRoot<>* partition) { 26 internal::ScopedGuard lock(lock_); 27 PA_DCHECK(partition); 28 auto it_and_whether_inserted = partitions_.insert(partition); 29 PA_DCHECK(it_and_whether_inserted.second); 30 } 31 UnregisterPartition(PartitionRoot<internal::ThreadSafe> * partition)32void MemoryReclaimer::UnregisterPartition( 33 PartitionRoot<internal::ThreadSafe>* partition) { 34 internal::ScopedGuard lock(lock_); 35 PA_DCHECK(partition); 36 size_t erased_count = partitions_.erase(partition); 37 PA_DCHECK(erased_count == 1u); 38 } 39 40 MemoryReclaimer::MemoryReclaimer() = default; 41 MemoryReclaimer::~MemoryReclaimer() = default; 42 ReclaimAll()43void MemoryReclaimer::ReclaimAll() { 44 constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | 45 PurgeFlags::kDiscardUnusedSystemPages | 46 PurgeFlags::kAggressiveReclaim; 47 Reclaim(kFlags); 48 } 49 ReclaimNormal()50void MemoryReclaimer::ReclaimNormal() { 51 constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | 52 PurgeFlags::kDiscardUnusedSystemPages; 53 Reclaim(kFlags); 54 } 55 Reclaim(int flags)56void MemoryReclaimer::Reclaim(int flags) { 57 internal::ScopedGuard lock( 58 lock_); // Has to protect from concurrent (Un)Register calls. 59 60 // PCScan quarantines freed slots. Trigger the scan first to let it call 61 // FreeNoHooksImmediate on slots that pass the quarantine. 62 // 63 // In turn, FreeNoHooksImmediate may add slots to thread cache. Purge it next 64 // so that the slots are actually freed. (This is done synchronously only for 65 // the current thread.) 66 // 67 // Lastly decommit empty slot spans and lastly try to discard unused pages at 68 // the end of the remaining active slots. 69 #if PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && BUILDFLAG(USE_STARSCAN) 70 { 71 using PCScan = internal::PCScan; 72 const auto invocation_mode = flags & PurgeFlags::kAggressiveReclaim 73 ? PCScan::InvocationMode::kForcedBlocking 74 : PCScan::InvocationMode::kBlocking; 75 PCScan::PerformScanIfNeeded(invocation_mode); 76 } 77 #endif // PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && 78 // BUILDFLAG(USE_STARSCAN) 79 80 #if PA_CONFIG(THREAD_CACHE_SUPPORTED) 81 // Don't completely empty the thread cache outside of low memory situations, 82 // as there is periodic purge which makes sure that it doesn't take too much 83 // space. 84 if (flags & PurgeFlags::kAggressiveReclaim) { 85 ThreadCacheRegistry::Instance().PurgeAll(); 86 } 87 #endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) 88 89 for (auto* partition : partitions_) { 90 partition->PurgeMemory(flags); 91 } 92 } 93 ResetForTesting()94void MemoryReclaimer::ResetForTesting() { 95 internal::ScopedGuard lock(lock_); 96 partitions_.clear(); 97 } 98 99 } // namespace partition_alloc 100