DiscardableMemoryManager::DiscardableMemoryManager(
size_t memory_limit,
- size_t bytes_to_keep_under_moderate_pressure)
+ size_t soft_memory_limit,
+ size_t bytes_to_keep_under_moderate_pressure,
+ TimeDelta hard_memory_limit_expiration_time)
: allocations_(AllocationMap::NO_AUTO_EVICT),
- bytes_allocated_(0),
+ bytes_allocated_(0u),
memory_limit_(memory_limit),
+ soft_memory_limit_(soft_memory_limit),
bytes_to_keep_under_moderate_pressure_(
- bytes_to_keep_under_moderate_pressure) {
- BytesAllocatedChanged();
+ bytes_to_keep_under_moderate_pressure),
+ hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) {
+ BytesAllocatedChanged(bytes_allocated_);
}
DiscardableMemoryManager::~DiscardableMemoryManager() {
void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
AutoLock lock(lock_);
memory_limit_ = bytes;
- EnforcePolicyWithLockAcquired();
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now(), memory_limit_);
+}
+
+void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
+ AutoLock lock(lock_);
+ soft_memory_limit_ = bytes;
}
void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
bytes_to_keep_under_moderate_pressure_ = bytes;
}
+void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
+ TimeDelta hard_memory_limit_expiration_time) {
+ AutoLock lock(lock_);
+ hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
+}
+
+bool DiscardableMemoryManager::ReduceMemoryUsage() {
+ return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
+}
+
void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
AutoLock lock(lock_);
// A registered memory listener is currently required. This DCHECK can be
size_t bytes_purgable = info.bytes;
DCHECK_LE(bytes_purgable, bytes_allocated_);
bytes_allocated_ -= bytes_purgable;
- BytesAllocatedChanged();
+ BytesAllocatedChanged(bytes_allocated_);
}
allocations_.Erase(it);
}
if (!info->bytes)
return false;
+ TimeTicks now = Now();
size_t bytes_required = info->purgable ? 0u : info->bytes;
if (memory_limit_) {
if (bytes_required < memory_limit_)
limit = memory_limit_ - bytes_required;
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit);
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
+ limit);
}
// Check for overflow.
*purged = !allocation->AllocateAndAcquireLock();
info->purgable = false;
+ info->last_usage = now;
if (bytes_required) {
bytes_allocated_ += bytes_required;
- BytesAllocatedChanged();
+ BytesAllocatedChanged(bytes_allocated_);
}
return true;
}
DCHECK(it != allocations_.end());
AllocationInfo* info = &it->second;
+ TimeTicks now = Now();
allocation->ReleaseLock();
info->purgable = true;
- EnforcePolicyWithLockAcquired();
+ info->last_usage = now;
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ now, memory_limit_);
}
void DiscardableMemoryManager::PurgeAll() {
AutoLock lock(lock_);
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
}
bool DiscardableMemoryManager::IsRegisteredForTest(
MemoryPressureListener::MemoryPressureLevel pressure_level) {
switch (pressure_level) {
case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
- Purge();
+ PurgeUntilWithinBytesToKeepUnderModeratePressure();
return;
case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
PurgeAll();
NOTREACHED();
}
-void DiscardableMemoryManager::Purge() {
+void
+DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() {
AutoLock lock(lock_);
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(
- bytes_to_keep_under_moderate_pressure_);
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now(), bytes_to_keep_under_moderate_pressure_);
}
-void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
- size_t limit) {
- TRACE_EVENT1(
- "base",
- "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
- "limit",
- limit);
+bool DiscardableMemoryManager::
+ PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
+ AutoLock lock(lock_);
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
+ return bytes_allocated_ <= soft_memory_limit_;
+}
+
+void DiscardableMemoryManager::
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ TimeTicks timestamp,
+ size_t limit) {
lock_.AssertAcquired();
size_t bytes_allocated_before_purging = bytes_allocated_;
if (bytes_allocated_ <= limit)
break;
- if (!info->purgable)
+
+ bool purgable = info->purgable && info->last_usage <= timestamp;
+ if (!purgable)
continue;
size_t bytes_purgable = info->bytes;
}
if (bytes_allocated_ != bytes_allocated_before_purging)
- BytesAllocatedChanged();
+ BytesAllocatedChanged(bytes_allocated_);
}
-void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_);
-}
-
-void DiscardableMemoryManager::BytesAllocatedChanged() const {
- TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_);
+void DiscardableMemoryManager::BytesAllocatedChanged(
+ size_t new_bytes_allocated) const {
+ TRACE_COUNTER_ID1(
+ "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
static const char kDiscardableMemoryUsageKey[] = "dm-usage";
base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
- Uint64ToString(bytes_allocated_));
+ Uint64ToString(new_bytes_allocated));
+}
+
+TimeTicks DiscardableMemoryManager::Now() const {
+ return TimeTicks::Now();
}
} // namespace internal