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 "partition_alloc/memory_reclaimer.h" 6 7 #include "partition_alloc/partition_alloc.h" 8 #include "partition_alloc/partition_alloc_base/no_destructor.h" 9 #include "partition_alloc/partition_alloc_buildflags.h" 10 #include "partition_alloc/partition_alloc_check.h" 11 #include "partition_alloc/partition_alloc_config.h" 12 13 #if BUILDFLAG(USE_STARSCAN) 14 #include "partition_alloc/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 * partition)32void MemoryReclaimer::UnregisterPartition(PartitionRoot* partition) { 33 internal::ScopedGuard lock(lock_); 34 PA_DCHECK(partition); 35 size_t erased_count = partitions_.erase(partition); 36 PA_DCHECK(erased_count == 1u); 37 } 38 39 MemoryReclaimer::MemoryReclaimer() = default; 40 MemoryReclaimer::~MemoryReclaimer() = default; 41 ReclaimAll()42void MemoryReclaimer::ReclaimAll() { 43 constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | 44 PurgeFlags::kDiscardUnusedSystemPages | 45 PurgeFlags::kAggressiveReclaim; 46 Reclaim(kFlags); 47 } 48 ReclaimNormal()49void MemoryReclaimer::ReclaimNormal() { 50 constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | 51 PurgeFlags::kDiscardUnusedSystemPages; 52 Reclaim(kFlags); 53 } 54 Reclaim(int flags)55void MemoryReclaimer::Reclaim(int flags) { 56 internal::ScopedGuard lock( 57 lock_); // Has to protect from concurrent (Un)Register calls. 58 59 // PCScan quarantines freed slots. Trigger the scan first to let it call 60 // FreeNoHooksImmediate on slots that pass the quarantine. 61 // 62 // In turn, FreeNoHooksImmediate may add slots to thread cache. Purge it next 63 // so that the slots are actually freed. (This is done synchronously only for 64 // the current thread.) 65 // 66 // Lastly decommit empty slot spans and lastly try to discard unused pages at 67 // the end of the remaining active slots. 68 #if PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && BUILDFLAG(USE_STARSCAN) 69 { 70 using PCScan = internal::PCScan; 71 const auto invocation_mode = flags & PurgeFlags::kAggressiveReclaim 72 ? PCScan::InvocationMode::kForcedBlocking 73 : PCScan::InvocationMode::kBlocking; 74 PCScan::PerformScanIfNeeded(invocation_mode); 75 } 76 #endif // PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && 77 // BUILDFLAG(USE_STARSCAN) 78 79 #if PA_CONFIG(THREAD_CACHE_SUPPORTED) 80 // Don't completely empty the thread cache outside of low memory situations, 81 // as there is periodic purge which makes sure that it doesn't take too much 82 // space. 83 if (flags & PurgeFlags::kAggressiveReclaim) { 84 ThreadCacheRegistry::Instance().PurgeAll(); 85 } 86 #endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) 87 88 for (auto* partition : partitions_) { 89 partition->PurgeMemory(flags); 90 } 91 } 92 ResetForTesting()93void MemoryReclaimer::ResetForTesting() { 94 internal::ScopedGuard lock(lock_); 95 partitions_.clear(); 96 } 97 98 } // namespace partition_alloc 99