summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorpkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 19:32:58 +0000
committerpkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 19:32:58 +0000
commit3148c5d6e03492bf0f0310981e3b87e281c22f89 (patch)
tree93fbf3989c3f582d6fca48199741312cbdcdbffb /chrome/browser
parent269a4ccc9ac806f5cf95603e4bd93c1d63bfb415 (diff)
downloadchromium_src-3148c5d6e03492bf0f0310981e3b87e281c22f89.zip
chromium_src-3148c5d6e03492bf0f0310981e3b87e281c22f89.tar.gz
chromium_src-3148c5d6e03492bf0f0310981e3b87e281c22f89.tar.bz2
Hook up the memory purger to all the relevant locations.
This removes the two-state purge/reset code (no longer necessary), and the hooks to the power monitor (not ready to turn those on without more work and testing). BUG=23400 TEST=Run Chrome with --purge-memory-button, use it for awhile, open the Task Manager, and click "Purge Memory". You should still be able to use the program normally, and hopefully we dumped some memory out too (varies by usage). Review URL: http://codereview.chromium.org/399028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32376 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser_main.cc3
-rw-r--r--chrome/browser/memory_purger.cc183
-rw-r--r--chrome/browser/memory_purger.h33
-rw-r--r--chrome/browser/views/task_manager_view.cc11
4 files changed, 153 insertions, 77 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 414e253..5f8c7fb 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -34,7 +34,6 @@
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
#include "chrome/browser/extensions/extension_protocols.h"
#include "chrome/browser/first_run.h"
-#include "chrome/browser/memory_purger.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/browser/net/dns_global.h"
#include "chrome/browser/net/metadata_url_request.h"
@@ -325,8 +324,6 @@ int BrowserMain(const MainFunctionParams& parameters) {
// timer settings. But it's necessary only on Windows.
base::Time::StartSystemMonitorObserver();
#endif // defined(OS_WIN)
- // Start the MemoryPurger, which observes the SystemMonitor as well.
- MemoryPurger::GetSingleton();
// Initialize statistical testing infrastructure.
FieldTrialList field_trial;
diff --git a/chrome/browser/memory_purger.cc b/chrome/browser/memory_purger.cc
index 2fc5b9e..456448a 100644
--- a/chrome/browser/memory_purger.cc
+++ b/chrome/browser/memory_purger.cc
@@ -4,58 +4,153 @@
#include "chrome/browser/memory_purger.h"
-#include "base/singleton.h"
+#include "base/thread.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/in_process_webkit/webkit_context.h"
+#include "chrome/browser/net/url_request_context_getter.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/browser/renderer_host/backing_store_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/common/render_messages.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/url_request/url_request_context.h"
+#include "third_party/tcmalloc/tcmalloc/src/google/malloc_extension.h"
+#include "v8/include/v8.h"
-// static
-MemoryPurger* MemoryPurger::GetSingleton() {
- return Singleton<MemoryPurger>::get();
+// PurgeMemoryHelper -----------------------------------------------------------
+
+// This is a small helper class used to ensure that the objects we want to use
+// on multiple threads are properly refed, so they don't get deleted out from
+// under us.
+class PurgeMemoryIOHelper
+ : public base::RefCountedThreadSafe<PurgeMemoryIOHelper> {
+ public:
+ explicit PurgeMemoryIOHelper(SafeBrowsingService* safe_browsing_service)
+ : safe_browsing_service_(safe_browsing_service) {
+ }
+
+ void AddRequestContextGetter(URLRequestContextGetter* request_context_getter);
+
+ void PurgeMemoryOnIOThread();
+
+ private:
+ typedef scoped_refptr<URLRequestContextGetter> RequestContextGetter;
+ typedef std::set<RequestContextGetter> RequestContextGetters;
+
+ RequestContextGetters request_context_getters_;
+ scoped_refptr<SafeBrowsingService> safe_browsing_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(PurgeMemoryIOHelper);
+};
+
+void PurgeMemoryIOHelper::AddRequestContextGetter(
+ URLRequestContextGetter* request_context_getter) {
+ if (!request_context_getters_.count(request_context_getter)) {
+ request_context_getters_.insert(
+ RequestContextGetter(request_context_getter));
+ }
}
-void MemoryPurger::OnSuspend() {
- // TODO:
- //
- // * For the browser process:
- // * Commit all the sqlite databases and unload them from memory; at the
- // very least, do this with the history system, then use the prefetch trick to
- // reload it on wake.
- // * Repeatedly call the V8 idle notification until it returns true
- // ("nothing more to free"). Note that it makes more sense to do this than to
- // implement a new "delete everything" pass because object references make it
- // difficult to free everything possible in just one pass.
- // * Dump the backing stores.
- // * Destroy the spellcheck object.
- // * Tell tcmalloc to free everything it can. (This is vague since it's not
- // clear to me what this means; Jim, Mike, and Anton will know more here.
- // Also, do we need to do this in each renderer process too?)
- //
- // * For each renderer process:
- // * Repeatedly call the V8 idle notification until it returns true.
- // * Tell the WebCore cache manager to set its global limit to 0, which
- // should flush the image/script/css/etc. caches.
- // * Destroy the WebCore glyph caches.
- // * Tell tcmalloc to free everything it can.
- //
- // Concern: If we tell a bunch of renderer processes to destroy their data,
- // they may have to page everything in to do it, which could end up
- // overflowing the amount of time we have to work with.
+void PurgeMemoryIOHelper::PurgeMemoryOnIOThread() {
+ // Ask ProxyServices to purge any memory they can (generally garbage in the
+ // wrapped ProxyResolver's JS engine).
+ for (RequestContextGetters::const_iterator i(
+ request_context_getters_.begin());
+ i != request_context_getters_.end(); ++i)
+ (*i)->GetURLRequestContext()->proxy_service()->PurgeMemory();
+
+ // Close the Safe Browsing database, freeing memory used to cache sqlite as
+ // well as a number of in-memory structures.
+ safe_browsing_service_->CloseDatabase();
}
-void MemoryPurger::OnResume() {
- // TODO:
- //
- // * For the browser process:
- // * Reload the history system and any other needed sqlite databases.
+// -----------------------------------------------------------------------------
+
+// static
+void MemoryPurger::PurgeAll() {
+ PurgeBrowser();
+ PurgeRenderers();
+
+ // TODO(pkasting):
+ // * Tell the plugin processes to release their free memory? Other stuff?
+ // * Enumerate what other processes exist and what to do for them.
+}
+
+// static
+void MemoryPurger::PurgeBrowser() {
+ // Dump the backing stores.
+ BackingStoreManager::RemoveAllBackingStores();
+
+ // Per-profile cleanup.
+ scoped_refptr<PurgeMemoryIOHelper> purge_memory_io_helper(
+ new PurgeMemoryIOHelper(g_browser_process->resource_dispatcher_host()->
+ safe_browsing_service()));
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ for (ProfileManager::iterator i(profile_manager->begin());
+ i != profile_manager->end(); ++i) {
+ Profile* profile = *i;
+ purge_memory_io_helper->AddRequestContextGetter(
+ profile->GetRequestContext());
+
+ // NOTE: Some objects below may be duplicates across profiles. We could
+ // conceivably put all these in sets and then iterate over the sets.
+
+ // Unload all history backends (freeing memory used to cache sqlite).
+ // Spinning up the history service is expensive, so we avoid doing it if it
+ // hasn't been done already.
+ HistoryService* history_service =
+ profile->GetHistoryServiceWithoutCreating();
+ if (history_service)
+ history_service->UnloadBackend();
+
+ // Unload all web databases (freeing memory used to cache sqlite).
+ WebDataService* web_data_service =
+ profile->GetWebDataServiceWithoutCreating();
+ if (web_data_service)
+ web_data_service->UnloadDatabase();
+
+ // Ask all WebKitContexts to purge memory (freeing memory used to cache
+ // the LocalStorage sqlite DB). WebKitContext creation is basically free so
+ // we don't bother with a "...WithoutCreating()" function.
+ profile->GetWebKitContext()->PurgeMemory();
+ }
+
+ ChromeThread::PostTask(ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(purge_memory_io_helper.get(),
+ &PurgeMemoryIOHelper::PurgeMemoryOnIOThread));
+
+ // TODO(pkasting):
+ // * Purge AppCache memory. Not yet implemented sufficiently.
+ // * Browser-side DatabaseTracker. Not implemented sufficiently.
+
+#if defined(OS_WIN)
+ // Tell tcmalloc to release any free pages it's still holding.
//
- // * For each renderer process:
- // * Reset the WebCore cache manager global limit to the default.
+ // TODO(pkasting): A lot of the above calls kick off actions on other threads.
+ // Maybe we should find a way to avoid calling this until those actions
+ // complete?
+ MallocExtension::instance()->ReleaseFreeMemory();
+#endif
}
-MemoryPurger::MemoryPurger() {
- base::SystemMonitor::Get()->AddObserver(this);
+// static
+void MemoryPurger::PurgeRenderers() {
+ // Direct all renderers to free everything they can.
+ //
+ // Concern: Telling a bunch of renderer processes to destroy their data may
+ // cause them to page everything in to do it, which could take a lot of time/
+ // cause jank.
+ for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance())
+ PurgeRendererForHost(i.GetCurrentValue());
}
-MemoryPurger::~MemoryPurger() {
- base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
- if (system_monitor)
- system_monitor->RemoveObserver(this);
+// static
+void MemoryPurger::PurgeRendererForHost(RenderProcessHost* host) {
+ // Direct the renderer to free everything it can.
+ host->Send(new ViewMsg_PurgeMemory());
}
diff --git a/chrome/browser/memory_purger.h b/chrome/browser/memory_purger.h
index 12cb821..2da3917 100644
--- a/chrome/browser/memory_purger.h
+++ b/chrome/browser/memory_purger.h
@@ -2,35 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// MemoryPurger is designed to be used a singleton which listens for
-// suspend/resume notifications and purges as much memory as possible before
-// suspend. The hope is that it will be faster to recalculate or manually
-// reload this data on resume than to let the OS page everything out and then
-// fault it back in.
+// MemoryPurger provides static APIs to purge as much memory as possible from
+// all processes. These can be hooked to various signals to try and balance
+// memory consumption, speed, page swapping, etc.
#ifndef CHROME_BROWSER_MEMORY_PURGER_H_
#define CHROME_BROWSER_MEMORY_PURGER_H_
-#include "base/system_monitor.h"
+#include "base/basictypes.h"
-template<typename Type>
-struct DefaultSingletonTraits;
+class RenderProcessHost;
+class SafeBrowsingService;
-class MemoryPurger : public base::SystemMonitor::PowerObserver {
+class MemoryPurger {
public:
- static MemoryPurger* GetSingleton();
-
- // PowerObserver
- virtual void OnSuspend();
- virtual void OnResume();
+ // Call any of these on the UI thread to purge memory from the named places.
+ static void PurgeAll();
+ static void PurgeBrowser();
+ static void PurgeRenderers();
+ static void PurgeRendererForHost(RenderProcessHost* host);
private:
- MemoryPurger();
- virtual ~MemoryPurger();
-
- friend struct DefaultSingletonTraits<MemoryPurger>;
-
- DISALLOW_COPY_AND_ASSIGN(MemoryPurger);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryPurger);
};
#endif // CHROME_BROWSER_MEMORY_PURGER_H_
diff --git a/chrome/browser/views/task_manager_view.cc b/chrome/browser/views/task_manager_view.cc
index bcfb422..3487f0e 100644
--- a/chrome/browser/views/task_manager_view.cc
+++ b/chrome/browser/views/task_manager_view.cc
@@ -254,7 +254,6 @@ class TaskManagerView : public views::View,
bool GetSavedAlwaysOnTopState(bool* always_on_top) const;
views::NativeButton* purge_memory_button_;
- bool purge_memory_button_in_purge_mode_;
views::NativeButton* kill_button_;
views::Link* about_memory_link_;
views::GroupTableView* tab_table_;
@@ -287,7 +286,6 @@ TaskManagerView* TaskManagerView::instance_ = NULL;
TaskManagerView::TaskManagerView()
: purge_memory_button_(NULL),
- purge_memory_button_in_purge_mode_(true),
task_manager_(TaskManager::GetInstance()),
model_(TaskManager::GetInstance()->model()),
is_always_on_top_(false) {
@@ -492,14 +490,7 @@ void TaskManagerView::Show() {
void TaskManagerView::ButtonPressed(
views::Button* sender, const views::Event& event) {
if (purge_memory_button_ && (sender == purge_memory_button_)) {
- if (purge_memory_button_in_purge_mode_) {
- MemoryPurger::GetSingleton()->OnSuspend();
- purge_memory_button_->SetLabel(L"Reset purger");
- } else {
- MemoryPurger::GetSingleton()->OnResume();
- purge_memory_button_->SetLabel(L"Purge Memory");
- }
- purge_memory_button_in_purge_mode_ = !purge_memory_button_in_purge_mode_;
+ MemoryPurger::PurgeAll();
} else {
DCHECK_EQ(sender, kill_button_);
for (views::TableSelectionIterator iter = tab_table_->SelectionBegin();