memory_purger.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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.
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(
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
55void PurgeMemoryIOHelper::AddRequestContextGetter(
56    scoped_refptr<net::URLRequestContextGetter> request_context_getter) {
57  request_context_getters_.insert(request_context_getter);
58}
59
60void 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
82void 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
92void 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
148void 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
160void MemoryPurger::PurgeRendererForHost(RenderProcessHost* host) {
161  // Direct the renderer to free everything it can.
162  host->Send(new ViewMsg_PurgeMemory());
163}
164