1 // Copyright (c) 2012 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 "chrome/browser/memory_purger.h" 6 7 #include <set> 8 9 #include "base/allocator/allocator_extension.h" 10 #include "base/bind.h" 11 #include "base/threading/thread.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/history/history_service.h" 14 #include "chrome/browser/history/history_service_factory.h" 15 #include "chrome/browser/profiles/profile_manager.h" 16 #include "chrome/browser/safe_browsing/database_manager.h" 17 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 18 #include "chrome/browser/ui/browser_list.h" 19 #include "chrome/browser/webdata/web_data_service.h" 20 #include "chrome/browser/webdata/web_data_service_factory.h" 21 #include "chrome/common/render_messages.h" 22 #include "content/public/browser/render_process_host.h" 23 #include "content/public/browser/render_widget_host.h" 24 #include "content/public/browser/resource_context.h" 25 #include "net/proxy/proxy_resolver.h" 26 #include "net/proxy/proxy_service.h" 27 #include "net/url_request/url_request_context.h" 28 #include "net/url_request/url_request_context_getter.h" 29 30 using content::BrowserContext; 31 using content::BrowserThread; 32 using content::ResourceContext; 33 34 // PurgeMemoryHelper ----------------------------------------------------------- 35 36 // This is a small helper class used to ensure that the objects we want to use 37 // on multiple threads are properly refed, so they don't get deleted out from 38 // under us. 39 class PurgeMemoryIOHelper 40 : public base::RefCountedThreadSafe<PurgeMemoryIOHelper> { 41 public: 42 PurgeMemoryIOHelper() { 43 safe_browsing_service_ = g_browser_process->safe_browsing_service(); 44 } 45 46 void AddRequestContextGetter( 47 scoped_refptr<net::URLRequestContextGetter> request_context_getter); 48 49 void PurgeMemoryOnIOThread(); 50 51 private: 52 friend class base::RefCountedThreadSafe<PurgeMemoryIOHelper>; 53 54 virtual ~PurgeMemoryIOHelper() {} 55 56 typedef scoped_refptr<net::URLRequestContextGetter> RequestContextGetter; 57 std::vector<RequestContextGetter> request_context_getters_; 58 59 scoped_refptr<SafeBrowsingService> safe_browsing_service_; 60 61 DISALLOW_COPY_AND_ASSIGN(PurgeMemoryIOHelper); 62 }; 63 64 void PurgeMemoryIOHelper::AddRequestContextGetter( 65 scoped_refptr<net::URLRequestContextGetter> request_context_getter) { 66 request_context_getters_.push_back(request_context_getter); 67 } 68 69 void PurgeMemoryIOHelper::PurgeMemoryOnIOThread() { 70 // Ask ProxyServices to purge any memory they can (generally garbage in the 71 // wrapped ProxyResolver's JS engine). 72 for (size_t i = 0; i < request_context_getters_.size(); ++i) { 73 request_context_getters_[i]->GetURLRequestContext()->proxy_service()-> 74 PurgeMemory(); 75 } 76 77 #if defined(FULL_SAFE_BROWSING) 78 safe_browsing_service_->database_manager()->PurgeMemory(); 79 #endif 80 } 81 82 // ----------------------------------------------------------------------------- 83 84 // static 85 void MemoryPurger::PurgeAll() { 86 PurgeBrowser(); 87 PurgeRenderers(); 88 89 // TODO(pkasting): 90 // * Tell the plugin processes to release their free memory? Other stuff? 91 // * Enumerate what other processes exist and what to do for them. 92 } 93 94 // static 95 void MemoryPurger::PurgeBrowser() { 96 // Dump the backing stores. 97 content::RenderWidgetHost::RemoveAllBackingStores(); 98 99 // Per-profile cleanup. 100 scoped_refptr<PurgeMemoryIOHelper> purge_memory_io_helper( 101 new PurgeMemoryIOHelper()); 102 ProfileManager* profile_manager = g_browser_process->profile_manager(); 103 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles()); 104 for (size_t i = 0; i < profiles.size(); ++i) { 105 purge_memory_io_helper->AddRequestContextGetter( 106 make_scoped_refptr(profiles[i]->GetRequestContext())); 107 108 // NOTE: Some objects below may be duplicates across profiles. We could 109 // conceivably put all these in sets and then iterate over the sets. 110 111 // Unload all history backends (freeing memory used to cache sqlite). 112 // Spinning up the history service is expensive, so we avoid doing it if it 113 // hasn't been done already. 114 HistoryService* history_service = 115 HistoryServiceFactory::GetForProfileWithoutCreating(profiles[i]); 116 if (history_service) 117 history_service->UnloadBackend(); 118 119 // Unload all web databases (freeing memory used to cache sqlite). 120 WebDataServiceWrapper* wds_wrapper = 121 WebDataServiceFactory::GetForProfileIfExists( 122 profiles[i], Profile::EXPLICIT_ACCESS); 123 if (wds_wrapper && wds_wrapper->GetWebData().get()) 124 wds_wrapper->GetWebData()->UnloadDatabase(); 125 126 BrowserContext::PurgeMemory(profiles[i]); 127 } 128 129 BrowserThread::PostTask( 130 BrowserThread::IO, FROM_HERE, 131 base::Bind(&PurgeMemoryIOHelper::PurgeMemoryOnIOThread, 132 purge_memory_io_helper.get())); 133 134 // TODO(pkasting): 135 // * Purge AppCache memory. Not yet implemented sufficiently. 136 // * Browser-side DatabaseTracker. Not implemented sufficiently. 137 138 // Tell our allocator to release any free pages it's still holding. 139 // 140 // TODO(pkasting): A lot of the above calls kick off actions on other threads. 141 // Maybe we should find a way to avoid calling this until those actions 142 // complete? 143 base::allocator::ReleaseFreeMemory(); 144 } 145 146 // static 147 void MemoryPurger::PurgeRenderers() { 148 // Direct all renderers to free everything they can. 149 // 150 // Concern: Telling a bunch of renderer processes to destroy their data may 151 // cause them to page everything in to do it, which could take a lot of time/ 152 // cause jank. 153 for (content::RenderProcessHost::iterator i( 154 content::RenderProcessHost::AllHostsIterator()); 155 !i.IsAtEnd(); i.Advance()) 156 PurgeRendererForHost(i.GetCurrentValue()); 157 } 158 159 // static 160 void MemoryPurger::PurgeRendererForHost(content::RenderProcessHost* host) { 161 // Direct the renderer to free everything it can. 162 host->Send(new ChromeViewMsg_PurgeMemory()); 163 } 164