1 // Copyright (c) 2011 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/threading/thread.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/history/history.h" 12 #include "chrome/browser/profiles/profile_manager.h" 13 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 14 #include "chrome/browser/ui/browser_list.h" 15 #include "chrome/browser/webdata/web_data_service.h" 16 #include "chrome/common/render_messages.h" 17 #include "content/browser/in_process_webkit/webkit_context.h" 18 #include "content/browser/renderer_host/backing_store_manager.h" 19 #include "content/browser/renderer_host/render_process_host.h" 20 #include "content/browser/renderer_host/resource_dispatcher_host.h" 21 #include "content/common/notification_service.h" 22 #include "net/proxy/proxy_resolver.h" 23 #include "net/url_request/url_request_context.h" 24 #include "net/url_request/url_request_context_getter.h" 25 #include "third_party/tcmalloc/chromium/src/google/malloc_extension.h" 26 #include "v8/include/v8.h" 27 28 // PurgeMemoryHelper ----------------------------------------------------------- 29 30 // This is a small helper class used to ensure that the objects we want to use 31 // on multiple threads are properly refed, so they don't get deleted out from 32 // under us. 33 class PurgeMemoryIOHelper 34 : public base::RefCountedThreadSafe<PurgeMemoryIOHelper> { 35 public: 36 explicit PurgeMemoryIOHelper(SafeBrowsingService* safe_browsing_service) 37 : safe_browsing_service_(safe_browsing_service) { 38 } 39 40 void AddRequestContextGetter( 41 scoped_refptr<net::URLRequestContextGetter> request_context_getter); 42 43 void PurgeMemoryOnIOThread(); 44 45 private: 46 typedef scoped_refptr<net::URLRequestContextGetter> RequestContextGetter; 47 typedef std::set<RequestContextGetter> RequestContextGetters; 48 49 RequestContextGetters request_context_getters_; 50 scoped_refptr<SafeBrowsingService> safe_browsing_service_; 51 52 DISALLOW_COPY_AND_ASSIGN(PurgeMemoryIOHelper); 53 }; 54 55 void PurgeMemoryIOHelper::AddRequestContextGetter( 56 scoped_refptr<net::URLRequestContextGetter> request_context_getter) { 57 request_context_getters_.insert(request_context_getter); 58 } 59 60 void PurgeMemoryIOHelper::PurgeMemoryOnIOThread() { 61 // Ask ProxyServices to purge any memory they can (generally garbage in the 62 // wrapped ProxyResolver's JS engine). 63 for (RequestContextGetters::const_iterator i( 64 request_context_getters_.begin()); 65 i != request_context_getters_.end(); ++i) 66 (*i)->GetURLRequestContext()->proxy_service()->PurgeMemory(); 67 68 // Close the Safe Browsing database, freeing memory used to cache sqlite as 69 // well as a number of in-memory structures. 70 safe_browsing_service_->CloseDatabase(); 71 72 // The appcache service listens for this notification. 73 NotificationService::current()->Notify( 74 NotificationType::PURGE_MEMORY, 75 Source<void>(NULL), 76 NotificationService::NoDetails()); 77 } 78 79 // ----------------------------------------------------------------------------- 80 81 // static 82 void MemoryPurger::PurgeAll() { 83 PurgeBrowser(); 84 PurgeRenderers(); 85 86 // TODO(pkasting): 87 // * Tell the plugin processes to release their free memory? Other stuff? 88 // * Enumerate what other processes exist and what to do for them. 89 } 90 91 // static 92 void MemoryPurger::PurgeBrowser() { 93 // Dump the backing stores. 94 BackingStoreManager::RemoveAllBackingStores(); 95 96 // Per-profile cleanup. 97 scoped_refptr<PurgeMemoryIOHelper> purge_memory_io_helper( 98 new PurgeMemoryIOHelper(g_browser_process->resource_dispatcher_host()-> 99 safe_browsing_service())); 100 ProfileManager* profile_manager = g_browser_process->profile_manager(); 101 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles()); 102 for (size_t i = 0; i < profiles.size(); ++i) { 103 purge_memory_io_helper->AddRequestContextGetter( 104 make_scoped_refptr(profiles[i]->GetRequestContext())); 105 106 // NOTE: Some objects below may be duplicates across profiles. We could 107 // conceivably put all these in sets and then iterate over the sets. 108 109 // Unload all history backends (freeing memory used to cache sqlite). 110 // Spinning up the history service is expensive, so we avoid doing it if it 111 // hasn't been done already. 112 HistoryService* history_service = 113 profiles[i]->GetHistoryServiceWithoutCreating(); 114 if (history_service) 115 history_service->UnloadBackend(); 116 117 // Unload all web databases (freeing memory used to cache sqlite). 118 WebDataService* web_data_service = 119 profiles[i]->GetWebDataServiceWithoutCreating(); 120 if (web_data_service) 121 web_data_service->UnloadDatabase(); 122 123 // Ask all WebKitContexts to purge memory (freeing memory used to cache 124 // the LocalStorage sqlite DB). WebKitContext creation is basically free so 125 // we don't bother with a "...WithoutCreating()" function. 126 profiles[i]->GetWebKitContext()->PurgeMemory(); 127 } 128 129 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 130 NewRunnableMethod(purge_memory_io_helper.get(), 131 &PurgeMemoryIOHelper::PurgeMemoryOnIOThread)); 132 133 // TODO(pkasting): 134 // * Purge AppCache memory. Not yet implemented sufficiently. 135 // * Browser-side DatabaseTracker. Not implemented sufficiently. 136 137 #if (defined(OS_WIN) || defined(OS_LINUX)) && defined(USE_TCMALLOC) 138 // Tell tcmalloc 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 MallocExtension::instance()->ReleaseFreeMemory(); 144 #endif 145 } 146 147 // static 148 void MemoryPurger::PurgeRenderers() { 149 // Direct all renderers to free everything they can. 150 // 151 // Concern: Telling a bunch of renderer processes to destroy their data may 152 // cause them to page everything in to do it, which could take a lot of time/ 153 // cause jank. 154 for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); 155 !i.IsAtEnd(); i.Advance()) 156 PurgeRendererForHost(i.GetCurrentValue()); 157 } 158 159 // static 160 void MemoryPurger::PurgeRendererForHost(RenderProcessHost* host) { 161 // Direct the renderer to free everything it can. 162 host->Send(new ViewMsg_PurgeMemory()); 163 } 164