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