1/*
2 * Copyright (C) 2011, 2014 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "MemoryRelease.h"
28
29#include "CSSFontSelector.h"
30#include "CSSValuePool.h"
31#include "CachedResourceLoader.h"
32#include "Chrome.h"
33#include "ChromeClient.h"
34#include "CommonVM.h"
35#include "Document.h"
36#include "FontCache.h"
37#include "Frame.h"
38#include "GCController.h"
39#include "HTMLMediaElement.h"
40#include "InlineStyleSheetOwner.h"
41#include "InspectorInstrumentation.h"
42#include "Logging.h"
43#include "MemoryCache.h"
44#include "Page.h"
45#include "PageCache.h"
46#include "RenderTheme.h"
47#include "ScrollingThread.h"
48#include "StyleScope.h"
49#include "StyledElement.h"
50#include "TextPainter.h"
51#include "WorkerThread.h"
52#include <wtf/FastMalloc.h>
53#include <wtf/SystemTracing.h>
54
55#if PLATFORM(COCOA)
56#include "ResourceUsageThread.h"
57#endif
58
59namespace WebCore {
60
61static void releaseNoncriticalMemory(MaintainMemoryCache maintainMemoryCache)
62{
63 RenderTheme::singleton().purgeCaches();
64
65 FontCache::singleton().purgeInactiveFontData();
66
67 clearWidthCaches();
68 TextPainter::clearGlyphDisplayLists();
69
70 for (auto* document : Document::allDocuments())
71 document->clearSelectorQueryCache();
72
73 if (maintainMemoryCache == MaintainMemoryCache::No)
74 MemoryCache::singleton().pruneDeadResourcesToSize(0);
75
76 InlineStyleSheetOwner::clearCache();
77}
78
79static void releaseCriticalMemory(Synchronous synchronous, MaintainPageCache maintainPageCache, MaintainMemoryCache maintainMemoryCache)
80{
81 // Right now, the only reason we call release critical memory while not under memory pressure is if the process is about to be suspended.
82 if (maintainPageCache == MaintainPageCache::No) {
83 PruningReason pruningReason = MemoryPressureHandler::singleton().isUnderMemoryPressure() ? PruningReason::MemoryPressure : PruningReason::ProcessSuspended;
84 PageCache::singleton().pruneToSizeNow(0, pruningReason);
85 }
86
87 if (maintainMemoryCache == MaintainMemoryCache::No) {
88 auto shouldDestroyDecodedDataForAllLiveResources = true;
89 MemoryCache::singleton().pruneLiveResourcesToSize(0, shouldDestroyDecodedDataForAllLiveResources);
90 }
91
92 CSSValuePool::singleton().drain();
93
94 for (auto& document : copyToVectorOf<RefPtr<Document>>(Document::allDocuments())) {
95 document->styleScope().releaseMemory();
96 document->fontSelector().emptyCaches();
97 document->cachedResourceLoader().garbageCollectDocumentResources();
98 }
99
100 GCController::singleton().deleteAllCode(JSC::DeleteAllCodeIfNotCollecting);
101
102#if ENABLE(VIDEO)
103 for (auto* mediaElement : HTMLMediaElement::allMediaElements()) {
104 if (mediaElement->paused())
105 mediaElement->purgeBufferedDataIfPossible();
106 }
107#endif
108
109 if (synchronous == Synchronous::Yes) {
110 GCController::singleton().garbageCollectNow();
111 } else {
112#if PLATFORM(IOS_FAMILY)
113 GCController::singleton().garbageCollectNowIfNotDoneRecently();
114#else
115 GCController::singleton().garbageCollectSoon();
116#endif
117 }
118}
119
120void releaseMemory(Critical critical, Synchronous synchronous, MaintainPageCache maintainPageCache, MaintainMemoryCache maintainMemoryCache)
121{
122 TraceScope scope(MemoryPressureHandlerStart, MemoryPressureHandlerEnd, static_cast<uint64_t>(critical), static_cast<uint64_t>(synchronous));
123
124 if (critical == Critical::Yes) {
125 // Return unused pages back to the OS now as this will likely give us a little memory to work with.
126 WTF::releaseFastMallocFreeMemory();
127 releaseCriticalMemory(synchronous, maintainPageCache, maintainMemoryCache);
128 }
129
130 releaseNoncriticalMemory(maintainMemoryCache);
131
132 platformReleaseMemory(critical);
133
134 if (synchronous == Synchronous::Yes) {
135 // FastMalloc has lock-free thread specific caches that can only be cleared from the thread itself.
136 WorkerThread::releaseFastMallocFreeMemoryInAllThreads();
137#if ENABLE(ASYNC_SCROLLING) && !PLATFORM(IOS_FAMILY)
138 ScrollingThread::dispatch(WTF::releaseFastMallocFreeMemory);
139#endif
140 WTF::releaseFastMallocFreeMemory();
141 }
142
143#if ENABLE(RESOURCE_USAGE)
144 Page::forEachPage([&](Page& page) {
145 InspectorInstrumentation::didHandleMemoryPressure(page, critical);
146 });
147#endif
148}
149
150#if !RELEASE_LOG_DISABLED
151static unsigned pageCount()
152{
153 unsigned count = 0;
154 Page::forEachPage([&] (Page& page) {
155 if (!page.isUtilityPage())
156 ++count;
157 });
158 return count;
159}
160#endif
161
162void logMemoryStatisticsAtTimeOfDeath()
163{
164#if !RELEASE_LOG_DISABLED
165#if PLATFORM(COCOA)
166 auto pageSize = vmPageSize();
167 auto pages = pagesPerVMTag();
168
169 RELEASE_LOG(MemoryPressure, "Dirty memory per VM tag at time of death:");
170 for (unsigned i = 0; i < 256; ++i) {
171 size_t dirty = pages[i].dirty * pageSize;
172 if (!dirty)
173 continue;
174 String tagName = displayNameForVMTag(i);
175 if (!tagName)
176 tagName = makeString("Tag ", i);
177 RELEASE_LOG(MemoryPressure, "%16s: %lu MB", tagName.latin1().data(), dirty / MB);
178 }
179#endif
180
181 auto& vm = commonVM();
182 RELEASE_LOG(MemoryPressure, "Memory usage statistics at time of death:");
183 RELEASE_LOG(MemoryPressure, "GC heap size: %zu", vm.heap.size());
184 RELEASE_LOG(MemoryPressure, "GC heap extra memory size: %zu", vm.heap.extraMemorySize());
185#if ENABLE(RESOURCE_USAGE)
186 RELEASE_LOG(MemoryPressure, "GC heap external memory: %zu", vm.heap.externalMemorySize());
187#endif
188 RELEASE_LOG(MemoryPressure, "Global object count: %zu", vm.heap.globalObjectCount());
189
190 RELEASE_LOG(MemoryPressure, "Page count: %u", pageCount());
191 RELEASE_LOG(MemoryPressure, "Document count: %u", Document::allDocuments().size());
192 RELEASE_LOG(MemoryPressure, "Live JavaScript objects:");
193 for (auto& it : *vm.heap.objectTypeCounts())
194 RELEASE_LOG(MemoryPressure, " %s: %d", it.key, it.value);
195#endif
196}
197
198#if !PLATFORM(COCOA)
199void platformReleaseMemory(Critical) { }
200void jettisonExpensiveObjectsOnTopLevelNavigation() { }
201void registerMemoryReleaseNotifyCallbacks() { }
202#endif
203
204} // namespace WebCore
205