1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ 6 #define BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ 7 8 #include "base/base_export.h" 9 #include "base/containers/hash_tables.h" 10 #include "base/containers/mru_cache.h" 11 #include "base/memory/memory_pressure_listener.h" 12 #include "base/synchronization/lock.h" 13 #include "base/time/time.h" 14 15 namespace base { 16 namespace internal { 17 18 // This interface is used by the DiscardableMemoryManager class to provide some 19 // level of userspace control over discardable memory allocations. 20 class DiscardableMemoryManagerAllocation { 21 public: 22 // Allocate and acquire a lock that prevents the allocation from being purged 23 // by the system. Returns true if memory was previously allocated and is still 24 // resident. 25 virtual bool AllocateAndAcquireLock() = 0; 26 27 // Release a previously acquired lock on the allocation so that it can be 28 // purged by the system. 29 virtual void ReleaseLock() = 0; 30 31 // Explicitly purge this allocation. It is illegal to call this while a lock 32 // is acquired on the allocation. 33 virtual void Purge() = 0; 34 35 protected: ~DiscardableMemoryManagerAllocation()36 virtual ~DiscardableMemoryManagerAllocation() {} 37 }; 38 39 } // namespace internal 40 } // namespace base 41 42 #if defined(COMPILER_GCC) 43 namespace BASE_HASH_NAMESPACE { 44 template <> 45 struct hash<base::internal::DiscardableMemoryManagerAllocation*> { 46 size_t operator()( 47 base::internal::DiscardableMemoryManagerAllocation* ptr) const { 48 return hash<size_t>()(reinterpret_cast<size_t>(ptr)); 49 } 50 }; 51 } // namespace BASE_HASH_NAMESPACE 52 #endif // COMPILER 53 54 namespace base { 55 namespace internal { 56 57 // The DiscardableMemoryManager manages a collection of 58 // DiscardableMemoryManagerAllocation instances. It is used on platforms that 59 // need some level of userspace control over discardable memory. It keeps track 60 // of all allocation instances (in case they need to be purged), and the total 61 // amount of allocated memory (in case this forces a purge). When memory usage 62 // reaches the limit, the manager purges the LRU memory. 63 // 64 // When notified of memory pressure, the manager either purges the LRU memory -- 65 // if the pressure is moderate -- or all discardable memory if the pressure is 66 // critical. 67 class BASE_EXPORT_PRIVATE DiscardableMemoryManager { 68 public: 69 typedef DiscardableMemoryManagerAllocation Allocation; 70 71 DiscardableMemoryManager(size_t memory_limit, 72 size_t soft_memory_limit, 73 size_t bytes_to_keep_under_moderate_pressure, 74 TimeDelta hard_memory_limit_expiration_time); 75 virtual ~DiscardableMemoryManager(); 76 77 // Call this to register memory pressure listener. Must be called on a thread 78 // with a MessageLoop current. 79 void RegisterMemoryPressureListener(); 80 81 // Call this to unregister memory pressure listener. 82 void UnregisterMemoryPressureListener(); 83 84 // The maximum number of bytes of memory that may be allocated before we force 85 // a purge. 86 void SetMemoryLimit(size_t bytes); 87 88 // The number of bytes of memory that may be allocated but unused for the hard 89 // limit expiration time without getting purged. 90 void SetSoftMemoryLimit(size_t bytes); 91 92 // Sets the amount of memory to keep when we're under moderate pressure. 93 void SetBytesToKeepUnderModeratePressure(size_t bytes); 94 95 // Sets the memory usage cutoff time for hard memory limit. 96 void SetHardMemoryLimitExpirationTime( 97 TimeDelta hard_memory_limit_expiration_time); 98 99 // This will attempt to reduce memory footprint until within soft memory 100 // limit. Returns true if there's no need to call this again until allocations 101 // have been used. 102 bool ReduceMemoryUsage(); 103 104 // Adds the given allocation to the manager's collection. 105 void Register(Allocation* allocation, size_t bytes); 106 107 // Removes the given allocation from the manager's collection. 108 void Unregister(Allocation* allocation); 109 110 // Returns false if an error occurred. Otherwise, returns true and sets 111 // |purged| to indicate whether or not allocation has been purged since last 112 // use. 113 bool AcquireLock(Allocation* allocation, bool* purged); 114 115 // Release a previously acquired lock on allocation. This allows the manager 116 // to purge it if necessary. 117 void ReleaseLock(Allocation* allocation); 118 119 // Purges all discardable memory. 120 void PurgeAll(); 121 122 // Returns true if allocation has been added to the manager's collection. This 123 // should only be used by tests. 124 bool IsRegisteredForTest(Allocation* allocation) const; 125 126 // Returns true if allocation can be purged. This should only be used by 127 // tests. 128 bool CanBePurgedForTest(Allocation* allocation) const; 129 130 // Returns total amount of allocated discardable memory. This should only be 131 // used by tests. 132 size_t GetBytesAllocatedForTest() const; 133 134 private: 135 struct AllocationInfo { 136 explicit AllocationInfo(size_t bytes) : bytes(bytes), purgable(false) {} 137 138 const size_t bytes; 139 bool purgable; 140 TimeTicks last_usage; 141 }; 142 typedef HashingMRUCache<Allocation*, AllocationInfo> AllocationMap; 143 144 // This can be called as a hint that the system is under memory pressure. 145 void OnMemoryPressure( 146 MemoryPressureListener::MemoryPressureLevel pressure_level); 147 148 // Purges memory until usage is less or equal to 149 // |bytes_to_keep_under_moderate_pressure_|. 150 void PurgeUntilWithinBytesToKeepUnderModeratePressure(); 151 152 // Purges memory not used since |hard_memory_limit_expiration_time_| before 153 // "right now" until usage is less or equal to |soft_memory_limit_|. 154 // Returns true if total amount of memory is less or equal to soft memory 155 // limit. 156 bool PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); 157 158 // Purges memory that has not been used since |timestamp| until usage is less 159 // or equal to |limit|. 160 // Caller must acquire |lock_| prior to calling this function. 161 void PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( 162 TimeTicks timestamp, 163 size_t limit); 164 165 // Called when a change to |bytes_allocated_| has been made. 166 void BytesAllocatedChanged(size_t new_bytes_allocated) const; 167 168 // Virtual for tests. 169 virtual TimeTicks Now() const; 170 171 // Needs to be held when accessing members. 172 mutable Lock lock_; 173 174 // A MRU cache of all allocated bits of memory. Used for purging. 175 AllocationMap allocations_; 176 177 // The total amount of allocated memory. 178 size_t bytes_allocated_; 179 180 // The maximum number of bytes of memory that may be allocated. 181 size_t memory_limit_; 182 183 // The number of bytes of memory that may be allocated but not used for 184 // |hard_memory_limit_expiration_time_| amount of time when receiving an idle 185 // notification. 186 size_t soft_memory_limit_; 187 188 // Under moderate memory pressure, we will purge memory until usage is within 189 // this limit. 190 size_t bytes_to_keep_under_moderate_pressure_; 191 192 // Allows us to be respond when the system reports that it is under memory 193 // pressure. 194 scoped_ptr<MemoryPressureListener> memory_pressure_listener_; 195 196 // Amount of time it takes for an allocation to become affected by 197 // |soft_memory_limit_|. 198 TimeDelta hard_memory_limit_expiration_time_; 199 200 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryManager); 201 }; 202 203 } // namespace internal 204 } // namespace base 205 206 #endif // BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ 207