Home | History | Annotate | Download | only in memory
      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 #include "base/memory/discardable_memory_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/containers/hash_tables.h"
      9 #include "base/containers/mru_cache.h"
     10 #include "base/debug/crash_logging.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/synchronization/lock.h"
     14 
     15 namespace base {
     16 namespace internal {
     17 
     18 DiscardableMemoryManager::DiscardableMemoryManager(
     19     size_t memory_limit,
     20     size_t soft_memory_limit,
     21     size_t bytes_to_keep_under_moderate_pressure,
     22     TimeDelta hard_memory_limit_expiration_time)
     23     : allocations_(AllocationMap::NO_AUTO_EVICT),
     24       bytes_allocated_(0u),
     25       memory_limit_(memory_limit),
     26       soft_memory_limit_(soft_memory_limit),
     27       bytes_to_keep_under_moderate_pressure_(
     28           bytes_to_keep_under_moderate_pressure),
     29       hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) {
     30   BytesAllocatedChanged(bytes_allocated_);
     31 }
     32 
     33 DiscardableMemoryManager::~DiscardableMemoryManager() {
     34   DCHECK(allocations_.empty());
     35   DCHECK_EQ(0u, bytes_allocated_);
     36 }
     37 
     38 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
     39   AutoLock lock(lock_);
     40   DCHECK(base::MessageLoop::current());
     41   DCHECK(!memory_pressure_listener_);
     42   memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind(
     43       &DiscardableMemoryManager::OnMemoryPressure, Unretained(this))));
     44 }
     45 
     46 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
     47   AutoLock lock(lock_);
     48   DCHECK(memory_pressure_listener_);
     49   memory_pressure_listener_.reset();
     50 }
     51 
     52 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
     53   AutoLock lock(lock_);
     54   memory_limit_ = bytes;
     55   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
     56       Now(), memory_limit_);
     57 }
     58 
     59 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
     60   AutoLock lock(lock_);
     61   soft_memory_limit_ = bytes;
     62 }
     63 
     64 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
     65     size_t bytes) {
     66   AutoLock lock(lock_);
     67   bytes_to_keep_under_moderate_pressure_ = bytes;
     68 }
     69 
     70 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
     71     TimeDelta hard_memory_limit_expiration_time) {
     72   AutoLock lock(lock_);
     73   hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
     74 }
     75 
     76 bool DiscardableMemoryManager::ReduceMemoryUsage() {
     77   return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
     78 }
     79 
     80 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
     81   AutoLock lock(lock_);
     82   // A registered memory listener is currently required. This DCHECK can be
     83   // moved or removed if we decide that it's useful to relax this condition.
     84   // TODO(reveman): Enable this DCHECK when skia and blink are able to
     85   // register memory pressure listeners. crbug.com/333907
     86   // DCHECK(memory_pressure_listener_);
     87   DCHECK(allocations_.Peek(allocation) == allocations_.end());
     88   allocations_.Put(allocation, AllocationInfo(bytes));
     89 }
     90 
     91 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
     92   AutoLock lock(lock_);
     93   AllocationMap::iterator it = allocations_.Peek(allocation);
     94   DCHECK(it != allocations_.end());
     95   const AllocationInfo& info = it->second;
     96 
     97   if (info.purgable) {
     98     size_t bytes_purgable = info.bytes;
     99     DCHECK_LE(bytes_purgable, bytes_allocated_);
    100     bytes_allocated_ -= bytes_purgable;
    101     BytesAllocatedChanged(bytes_allocated_);
    102   }
    103   allocations_.Erase(it);
    104 }
    105 
    106 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
    107                                            bool* purged) {
    108   AutoLock lock(lock_);
    109   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
    110   // cache.
    111   AllocationMap::iterator it = allocations_.Get(allocation);
    112   DCHECK(it != allocations_.end());
    113   AllocationInfo* info = &it->second;
    114 
    115   if (!info->bytes)
    116     return false;
    117 
    118   TimeTicks now = Now();
    119   size_t bytes_required = info->purgable ? 0u : info->bytes;
    120 
    121   if (memory_limit_) {
    122     size_t limit = 0;
    123     if (bytes_required < memory_limit_)
    124       limit = memory_limit_ - bytes_required;
    125 
    126     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
    127                                                                         limit);
    128   }
    129 
    130   // Check for overflow.
    131   if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
    132     return false;
    133 
    134   *purged = !allocation->AllocateAndAcquireLock();
    135   info->purgable = false;
    136   info->last_usage = now;
    137   if (bytes_required) {
    138     bytes_allocated_ += bytes_required;
    139     BytesAllocatedChanged(bytes_allocated_);
    140   }
    141   return true;
    142 }
    143 
    144 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
    145   AutoLock lock(lock_);
    146   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
    147   // cache.
    148   AllocationMap::iterator it = allocations_.Get(allocation);
    149   DCHECK(it != allocations_.end());
    150   AllocationInfo* info = &it->second;
    151 
    152   TimeTicks now = Now();
    153   allocation->ReleaseLock();
    154   info->purgable = true;
    155   info->last_usage = now;
    156 
    157   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
    158       now, memory_limit_);
    159 }
    160 
    161 void DiscardableMemoryManager::PurgeAll() {
    162   AutoLock lock(lock_);
    163   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
    164 }
    165 
    166 bool DiscardableMemoryManager::IsRegisteredForTest(
    167     Allocation* allocation) const {
    168   AutoLock lock(lock_);
    169   AllocationMap::const_iterator it = allocations_.Peek(allocation);
    170   return it != allocations_.end();
    171 }
    172 
    173 bool DiscardableMemoryManager::CanBePurgedForTest(
    174     Allocation* allocation) const {
    175   AutoLock lock(lock_);
    176   AllocationMap::const_iterator it = allocations_.Peek(allocation);
    177   return it != allocations_.end() && it->second.purgable;
    178 }
    179 
    180 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
    181   AutoLock lock(lock_);
    182   return bytes_allocated_;
    183 }
    184 
    185 void DiscardableMemoryManager::OnMemoryPressure(
    186     MemoryPressureListener::MemoryPressureLevel pressure_level) {
    187   switch (pressure_level) {
    188     case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
    189       PurgeUntilWithinBytesToKeepUnderModeratePressure();
    190       return;
    191     case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
    192       PurgeAll();
    193       return;
    194   }
    195 
    196   NOTREACHED();
    197 }
    198 
    199 void
    200 DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() {
    201   AutoLock lock(lock_);
    202 
    203   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
    204       Now(), bytes_to_keep_under_moderate_pressure_);
    205 }
    206 
    207 bool DiscardableMemoryManager::
    208     PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
    209   AutoLock lock(lock_);
    210 
    211   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
    212       Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
    213 
    214   return bytes_allocated_ <= soft_memory_limit_;
    215 }
    216 
    217 void DiscardableMemoryManager::
    218     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
    219         TimeTicks timestamp,
    220         size_t limit) {
    221   lock_.AssertAcquired();
    222 
    223   size_t bytes_allocated_before_purging = bytes_allocated_;
    224   for (AllocationMap::reverse_iterator it = allocations_.rbegin();
    225        it != allocations_.rend();
    226        ++it) {
    227     Allocation* allocation = it->first;
    228     AllocationInfo* info = &it->second;
    229 
    230     if (bytes_allocated_ <= limit)
    231       break;
    232 
    233     bool purgable = info->purgable && info->last_usage <= timestamp;
    234     if (!purgable)
    235       continue;
    236 
    237     size_t bytes_purgable = info->bytes;
    238     DCHECK_LE(bytes_purgable, bytes_allocated_);
    239     bytes_allocated_ -= bytes_purgable;
    240     info->purgable = false;
    241     allocation->Purge();
    242   }
    243 
    244   if (bytes_allocated_ != bytes_allocated_before_purging)
    245     BytesAllocatedChanged(bytes_allocated_);
    246 }
    247 
    248 void DiscardableMemoryManager::BytesAllocatedChanged(
    249     size_t new_bytes_allocated) const {
    250   TRACE_COUNTER_ID1(
    251       "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
    252 
    253   static const char kDiscardableMemoryUsageKey[] = "dm-usage";
    254   base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
    255                                 Uint64ToString(new_bytes_allocated));
    256 }
    257 
    258 TimeTicks DiscardableMemoryManager::Now() const {
    259   return TimeTicks::Now();
    260 }
    261 
    262 }  // namespace internal
    263 }  // namespace base
    264