From f5965a35d658e81d95301b8e96acc07474133031 Mon Sep 17 00:00:00 2001 From: "reveman@chromium.org" Date: Mon, 11 Aug 2014 21:09:37 +0000 Subject: [PATCH] base: Introduce an explicit call for reducing emulated discardable memory usage. This removes the use of memory pressure signals from discardable memory system and instead introduces an explicit call for reducing emulated discardable memory that the renderer can call when all widgets are hidden until we have a better mechanism in place. BUG=398087 TEST=base_unittests,content_browsertests Review URL: https://codereview.chromium.org/448173002 Cr-Commit-Position: refs/heads/master@{#288793} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288793 0039d316-1c4b-4281-b951-d872f2087c98 --- base/memory/discardable_memory.h | 8 --- base/memory/discardable_memory_android.cc | 15 +---- base/memory/discardable_memory_emulated.cc | 16 ++---- base/memory/discardable_memory_emulated.h | 7 ++- base/memory/discardable_memory_linux.cc | 10 ---- base/memory/discardable_memory_mac.cc | 16 +----- base/memory/discardable_memory_manager.cc | 56 ++----------------- base/memory/discardable_memory_manager.h | 36 ++---------- .../discardable_memory_manager_unittest.cc | 41 +++++--------- base/memory/discardable_memory_unittest.cc | 10 +--- base/memory/discardable_memory_win.cc | 10 ---- content/renderer/render_thread_impl.cc | 14 +++-- .../render_thread_impl_browsertest.cc | 52 +++++++++++++++++ 13 files changed, 98 insertions(+), 193 deletions(-) diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h index d16ed3e8480f93..5b74705234ff6c 100644 --- a/base/memory/discardable_memory.h +++ b/base/memory/discardable_memory.h @@ -64,14 +64,6 @@ class BASE_EXPORT DiscardableMemory { public: virtual ~DiscardableMemory() {} - // Call this on a thread with a MessageLoop current to allow discardable - // memory implementations to respond to memory pressure signals. - static void RegisterMemoryPressureListeners(); - - // Call this to prevent discardable memory implementations from responding - // to memory pressure signals. - static void UnregisterMemoryPressureListeners(); - // Gets the discardable memory type with a given name. static DiscardableMemoryType GetNamedType(const std::string& name); diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc index 8988c2b8d2bd46..acf29ac85f11eb 100644 --- a/base/memory/discardable_memory_android.cc +++ b/base/memory/discardable_memory_android.cc @@ -32,10 +32,7 @@ size_t GetOptimalAshmemRegionSizeForAllocator() { // Holds the shared state used for allocations. struct SharedState { SharedState() - : manager(kAshmemMemoryLimit, - kAshmemMemoryLimit, - kAshmemMemoryLimit, - TimeDelta::Max()), + : manager(kAshmemMemoryLimit, kAshmemMemoryLimit, TimeDelta::Max()), allocator(kAshmemAllocatorName, GetOptimalAshmemRegionSizeForAllocator()) {} @@ -46,16 +43,6 @@ LazyInstance::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc index 340a181834ad3d..f9097b186e1405 100644 --- a/base/memory/discardable_memory_emulated.cc +++ b/base/memory/discardable_memory_emulated.cc @@ -13,14 +13,12 @@ namespace { // This is admittedly pretty magical. const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; -const size_t kEmulatedBytesToKeepUnderModeratePressure = 4 * 1024 * 1024; const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; struct SharedState { SharedState() : manager(kEmulatedMemoryLimit, kEmulatedSoftMemoryLimit, - kEmulatedBytesToKeepUnderModeratePressure, TimeDelta::FromMilliseconds( kEmulatedHardMemoryLimitExpirationTimeMs)) {} @@ -45,18 +43,14 @@ DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { } // static -void DiscardableMemoryEmulated::RegisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.RegisterMemoryPressureListener(); -} - -// static -void DiscardableMemoryEmulated::UnregisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.UnregisterMemoryPressureListener(); +bool DiscardableMemoryEmulated::ReduceMemoryUsage() { + return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); } // static -bool DiscardableMemoryEmulated::ReduceMemoryUsage() { - return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); +void DiscardableMemoryEmulated::ReduceMemoryUsageUntilWithinLimit( + size_t bytes) { + g_shared_state.Pointer()->manager.ReduceMemoryUsageUntilWithinLimit(bytes); } // static diff --git a/base/memory/discardable_memory_emulated.h b/base/memory/discardable_memory_emulated.h index 64e99511b7caad..d928513527ea89 100644 --- a/base/memory/discardable_memory_emulated.h +++ b/base/memory/discardable_memory_emulated.h @@ -19,10 +19,13 @@ class DiscardableMemoryEmulated explicit DiscardableMemoryEmulated(size_t bytes); virtual ~DiscardableMemoryEmulated(); - static void RegisterMemoryPressureListeners(); - static void UnregisterMemoryPressureListeners(); static bool ReduceMemoryUsage(); + // TODO(reveman): Remove this as it is breaking the discardable memory design + // principle that implementations should not rely on information this is + // unavailable in kernel space. crbug.com/400423 + BASE_EXPORT static void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + static void PurgeForTesting(); bool Initialize(); diff --git a/base/memory/discardable_memory_linux.cc b/base/memory/discardable_memory_linux.cc index b9342e92043cad..578b2c1c756049 100644 --- a/base/memory/discardable_memory_linux.cc +++ b/base/memory/discardable_memory_linux.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc index b2184e7d589c41..fa6a23145c389b 100644 --- a/base/memory/discardable_memory_mac.cc +++ b/base/memory/discardable_memory_mac.cc @@ -25,11 +25,7 @@ namespace { const size_t kMacMemoryLimit = 512 * 1024 * 1024; struct SharedState { - SharedState() - : manager(kMacMemoryLimit, - kMacMemoryLimit, - kMacMemoryLimit, - TimeDelta::Max()) {} + SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit, TimeDelta::Max()) {} internal::DiscardableMemoryManager manager; }; @@ -159,16 +155,6 @@ class DiscardableMemoryMac } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_manager.cc b/base/memory/discardable_memory_manager.cc index d976da203c4a88..3647b7b2f91a68 100644 --- a/base/memory/discardable_memory_manager.cc +++ b/base/memory/discardable_memory_manager.cc @@ -18,14 +18,11 @@ namespace internal { DiscardableMemoryManager::DiscardableMemoryManager( size_t memory_limit, 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_(0u), memory_limit_(memory_limit), soft_memory_limit_(soft_memory_limit), - bytes_to_keep_under_moderate_pressure_( - bytes_to_keep_under_moderate_pressure), hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) { BytesAllocatedChanged(bytes_allocated_); } @@ -35,20 +32,6 @@ DiscardableMemoryManager::~DiscardableMemoryManager() { DCHECK_EQ(0u, bytes_allocated_); } -void DiscardableMemoryManager::RegisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(base::MessageLoop::current()); - DCHECK(!memory_pressure_listener_); - memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( - &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); -} - -void DiscardableMemoryManager::UnregisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(memory_pressure_listener_); - memory_pressure_listener_.reset(); -} - void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { AutoLock lock(lock_); memory_limit_ = bytes; @@ -61,12 +44,6 @@ void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) { soft_memory_limit_ = bytes; } -void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( - size_t bytes) { - AutoLock lock(lock_); - bytes_to_keep_under_moderate_pressure_ = bytes; -} - void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time) { AutoLock lock(lock_); @@ -77,13 +54,14 @@ bool DiscardableMemoryManager::ReduceMemoryUsage() { return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); } +void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + AutoLock lock(lock_); + PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), + bytes); +} + void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { AutoLock lock(lock_); - // A registered memory listener is currently required. This DCHECK can be - // moved or removed if we decide that it's useful to relax this condition. - // TODO(reveman): Enable this DCHECK when skia and blink are able to - // register memory pressure listeners. crbug.com/333907 - // DCHECK(memory_pressure_listener_); DCHECK(allocations_.Peek(allocation) == allocations_.end()); allocations_.Put(allocation, AllocationInfo(bytes)); } @@ -182,28 +160,6 @@ size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { return bytes_allocated_; } -void DiscardableMemoryManager::OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level) { - switch (pressure_level) { - case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: - PurgeUntilWithinBytesToKeepUnderModeratePressure(); - return; - case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: - PurgeAll(); - return; - } - - NOTREACHED(); -} - -void -DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { - AutoLock lock(lock_); - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - Now(), bytes_to_keep_under_moderate_pressure_); -} - bool DiscardableMemoryManager:: PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { AutoLock lock(lock_); diff --git a/base/memory/discardable_memory_manager.h b/base/memory/discardable_memory_manager.h index a61f141c43034e..94b3c55108f227 100644 --- a/base/memory/discardable_memory_manager.h +++ b/base/memory/discardable_memory_manager.h @@ -8,7 +8,6 @@ #include "base/base_export.h" #include "base/containers/hash_tables.h" #include "base/containers/mru_cache.h" -#include "base/memory/memory_pressure_listener.h" #include "base/synchronization/lock.h" #include "base/time/time.h" @@ -60,27 +59,15 @@ namespace internal { // of all allocation instances (in case they need to be purged), and the total // amount of allocated memory (in case this forces a purge). When memory usage // reaches the limit, the manager purges the LRU memory. -// -// When notified of memory pressure, the manager either purges the LRU memory -- -// if the pressure is moderate -- or all discardable memory if the pressure is -// critical. class BASE_EXPORT_PRIVATE DiscardableMemoryManager { public: typedef DiscardableMemoryManagerAllocation Allocation; DiscardableMemoryManager(size_t memory_limit, size_t soft_memory_limit, - size_t bytes_to_keep_under_moderate_pressure, TimeDelta hard_memory_limit_expiration_time); virtual ~DiscardableMemoryManager(); - // Call this to register memory pressure listener. Must be called on a thread - // with a MessageLoop current. - void RegisterMemoryPressureListener(); - - // Call this to unregister memory pressure listener. - void UnregisterMemoryPressureListener(); - // The maximum number of bytes of memory that may be allocated before we force // a purge. void SetMemoryLimit(size_t bytes); @@ -89,9 +76,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // limit expiration time without getting purged. void SetSoftMemoryLimit(size_t bytes); - // Sets the amount of memory to keep when we're under moderate pressure. - void SetBytesToKeepUnderModeratePressure(size_t bytes); - // Sets the memory usage cutoff time for hard memory limit. void SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time); @@ -101,6 +85,10 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // have been used. bool ReduceMemoryUsage(); + // This can be called to attempt to reduce memory footprint until within + // limit for bytes to keep under moderate pressure. + void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + // Adds the given allocation to the manager's collection. void Register(Allocation* allocation, size_t bytes); @@ -141,14 +129,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { }; typedef HashingMRUCache AllocationMap; - // This can be called as a hint that the system is under memory pressure. - void OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level); - - // Purges memory until usage is less or equal to - // |bytes_to_keep_under_moderate_pressure_|. - void PurgeUntilWithinBytesToKeepUnderModeratePressure(); - // Purges memory not used since |hard_memory_limit_expiration_time_| before // "right now" until usage is less or equal to |soft_memory_limit_|. // Returns true if total amount of memory is less or equal to soft memory @@ -185,14 +165,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // notification. size_t soft_memory_limit_; - // Under moderate memory pressure, we will purge memory until usage is within - // this limit. - size_t bytes_to_keep_under_moderate_pressure_; - - // Allows us to be respond when the system reports that it is under memory - // pressure. - scoped_ptr memory_pressure_listener_; - // Amount of time it takes for an allocation to become affected by // |soft_memory_limit_|. TimeDelta hard_memory_limit_expiration_time_; diff --git a/base/memory/discardable_memory_manager_unittest.cc b/base/memory/discardable_memory_manager_unittest.cc index ef5739a6526a5e..674499fe0ea0bd 100644 --- a/base/memory/discardable_memory_manager_unittest.cc +++ b/base/memory/discardable_memory_manager_unittest.cc @@ -5,7 +5,6 @@ #include "base/memory/discardable_memory_manager.h" #include "base/bind.h" -#include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,7 +45,6 @@ class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { // something else needs to explicit set the limit. const size_t kDefaultMemoryLimit = 1024; const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit; -const size_t kDefaultBytesToKeepUnderModeratePressure = kDefaultMemoryLimit; class TestDiscardableMemoryManagerImpl : public internal::DiscardableMemoryManager { @@ -54,7 +52,6 @@ class TestDiscardableMemoryManagerImpl TestDiscardableMemoryManagerImpl() : DiscardableMemoryManager(kDefaultMemoryLimit, kDefaultSoftMemoryLimit, - kDefaultBytesToKeepUnderModeratePressure, TimeDelta::Max()) {} void SetNow(TimeTicks now) { now_ = now; } @@ -68,9 +65,7 @@ class TestDiscardableMemoryManagerImpl class DiscardableMemoryManagerTestBase { public: - DiscardableMemoryManagerTestBase() { - manager_.RegisterMemoryPressureListener(); - } + DiscardableMemoryManagerTestBase() {} protected: enum LockStatus { @@ -85,10 +80,6 @@ class DiscardableMemoryManagerTestBase { void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); } - void SetBytesToKeepUnderModeratePressure(size_t bytes) { - manager_.SetBytesToKeepUnderModeratePressure(bytes); - } - void SetHardMemoryLimitExpirationTime(TimeDelta time) { manager_.SetHardMemoryLimitExpirationTime(time); } @@ -127,10 +118,15 @@ class DiscardableMemoryManagerTestBase { void SetNow(TimeTicks now) { manager_.SetNow(now); } + void PurgeAll() { return manager_.PurgeAll(); } + bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } + void ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + manager_.ReduceMemoryUsageUntilWithinLimit(bytes); + } + private: - MessageLoopForIO message_loop_; TestDiscardableMemoryManagerImpl manager_; }; @@ -192,11 +188,7 @@ TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { EXPECT_TRUE(CanBePurged(&allocation)); // Force the system to purge. - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - - // Required because ObserverListThreadSafe notifies via PostTask. - RunLoop().RunUntilIdle(); + PurgeAll(); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); EXPECT_FALSE(CanBePurged(&allocation)); @@ -301,17 +293,14 @@ class DiscardableMemoryManagerPermutationTest TestAllocationImpl allocation_[3]; }; -// Verify that memory was discarded in the correct order after applying -// memory pressure. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { +// Verify that memory was discarded in the correct order after reducing usage to +// limit. +TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscarded) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_MODERATE); - RunLoop().RunUntilIdle(); + ReduceMemoryUsageUntilWithinLimit(1024); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); @@ -326,7 +315,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); @@ -340,7 +328,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { // Verify that no more memory than necessary was discarded after changing // memory limit. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { - SetBytesToKeepUnderModeratePressure(2048); SetMemoryLimit(4096); RegisterAndUseAllocations(); @@ -358,9 +345,7 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { RegisterAndUseAllocations(); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - RunLoop().RunUntilIdle(); + PurgeAll(); for (int i = 0; i < 3; ++i) { if (i == 0) diff --git a/base/memory/discardable_memory_unittest.cc b/base/memory/discardable_memory_unittest.cc index dc0e2cd21262b4..516a96b5a4fa25 100644 --- a/base/memory/discardable_memory_unittest.cc +++ b/base/memory/discardable_memory_unittest.cc @@ -6,7 +6,6 @@ #include -#include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_ANDROID) @@ -19,12 +18,8 @@ namespace { class DiscardableMemoryTest : public testing::TestWithParam { public: - DiscardableMemoryTest() : message_loop_(MessageLoop::TYPE_IO) { - // Register memory pressure listeners now that we have a message loop. - DiscardableMemory::RegisterMemoryPressureListeners(); - } + DiscardableMemoryTest() {} virtual ~DiscardableMemoryTest() { - DiscardableMemory::UnregisterMemoryPressureListeners(); } protected: @@ -32,9 +27,6 @@ class DiscardableMemoryTest return DiscardableMemory::CreateLockedMemoryWithType( GetParam(), size).Pass(); } - - private: - MessageLoop message_loop_; }; const size_t kSize = 1024; diff --git a/base/memory/discardable_memory_win.cc b/base/memory/discardable_memory_win.cc index b9342e92043cad..578b2c1c756049 100644 --- a/base/memory/discardable_memory_win.cc +++ b/base/memory/discardable_memory_win.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 37fa0900916969..daff6bc8dad3e7 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -15,6 +15,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/discardable_memory.h" +#include "base/memory/discardable_memory_emulated.h" #include "base/memory/shared_memory.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" @@ -179,6 +180,9 @@ const int kMaxRasterThreads = 64; // allocation that exceeds this limit. const size_t kImageCacheSingleAllocationByteLimit = 64 * 1024 * 1024; +const size_t kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden = + 4 * 1024 * 1024; + // Keep the global RenderThreadImpl in a TLS slot so it is impossible to access // incorrectly from the wrong thread. base::LazyInstance > @@ -545,10 +549,6 @@ void RenderThreadImpl::Init() { base::DiscardableMemory::SetPreferredType(type); - // Allow discardable memory implementations to register memory pressure - // listeners. - base::DiscardableMemory::RegisterMemoryPressureListeners(); - // AllocateGpuMemoryBuffer must be used exclusively on one thread but // it doesn't have to be the same thread RenderThreadImpl is created on. allocate_gpu_memory_buffer_thread_checker_.DetachFromThread(); @@ -1623,6 +1623,12 @@ void RenderThreadImpl::WidgetHidden() { hidden_widget_count_++; if (widget_count_ && hidden_widget_count_ == widget_count_) { + // TODO(reveman): Remove this when we have a better mechanism to prevent + // total discardable memory used by all renderers from growing too large. + base::internal::DiscardableMemoryEmulated:: + ReduceMemoryUsageUntilWithinLimit( + kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden); + if (GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) ScheduleIdleHandler(kInitialIdleHandlerDelayMs); } diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc index f5a0d1d7f7df75..55a4379c25505c 100644 --- a/content/renderer/render_thread_impl_browsertest.cc +++ b/content/renderer/render_thread_impl_browsertest.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "base/memory/discardable_memory.h" +#include "base/memory/scoped_vector.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" @@ -70,5 +72,55 @@ TEST_F(RenderThreadImplBrowserTest, base::Bind(&CheckRenderThreadInputHandlerManager, thread)); } +// Checks that emulated discardable memory is discarded when the last widget +// is hidden. +TEST_F(RenderThreadImplBrowserTest, + EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden) { + ContentClient content_client; + ContentBrowserClient content_browser_client; + ContentRendererClient content_renderer_client; + SetContentClient(&content_client); + SetBrowserClientForTesting(&content_browser_client); + SetRendererClientForTesting(&content_renderer_client); + base::MessageLoopForIO message_loop_; + + std::string channel_id = + IPC::Channel::GenerateVerifiedChannelID(std::string()); + DummyListener dummy_listener; + scoped_ptr channel( + IPC::Channel::CreateServer(channel_id, &dummy_listener)); + ASSERT_TRUE(channel->Connect()); + + scoped_ptr mock_process(new MockRenderProcess); + // Owned by mock_process. + RenderThreadImpl* thread = new RenderThreadImpl(channel_id); + thread->EnsureWebKitInitialized(); + thread->WidgetCreated(); + + // Allocate 128MB of discardable memory. + ScopedVector discardable_memory; + for (int i = 0; i < 32; ++i) { + discardable_memory.push_back( + base::DiscardableMemory::CreateLockedMemoryWithType( + base::DISCARDABLE_MEMORY_TYPE_EMULATED, 4 * 1024 * 1024).release()); + ASSERT_TRUE(discardable_memory.back()); + discardable_memory.back()->Unlock(); + } + + // Hide all widgets. + thread->WidgetHidden(); + + // Count how much memory is left, should be at most one block. + int blocks_left = 0; + for (auto iter = discardable_memory.begin(); iter != discardable_memory.end(); + ++iter) { + if ((*iter)->Lock() == base::DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS) + ++blocks_left; + } + EXPECT_LE(blocks_left, 1); + + thread->WidgetDestroyed(); +} + } // namespace } // namespace content